Skip to content

Commit c5dd9d1

Browse files
authored
Merge pull request #117 from woocommerce/add/term-generator
Add term generator
2 parents 768ca48 + a73883b commit c5dd9d1

File tree

3 files changed

+346
-0
lines changed

3 files changed

+346
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,17 @@ Generate coupons with a maximum discount amount.
5151

5252
Generate customers based on the number of customers parameter.
5353
- `wp wc generate customers <nr of customers>`
54+
55+
### Terms
56+
57+
Generate terms in the Product Categories taxonomy based on the number of terms parameter.
58+
- `wp wc generate terms product_cat <nr of terms>`
59+
60+
Generate hierarchical product categories with a maximum number of sub-levels.
61+
- `wp wc generate terms product_cat <nr of terms> --max-depth=5`
62+
63+
Generate product categories that are all child terms of an existing product category term.
64+
- `wp wc generate terms product_cat <nr of terms> --parent=123`
65+
66+
Generate terms in the Product Tags taxonomy based on the number of terms parameter.
67+
- `wp wc generate terms product_tag <nr of terms>`

includes/CLI.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,43 @@ public static function coupons( $args, $assoc_args ) {
196196

197197
WP_CLI::success( $amount . ' coupons generated in ' . $display_time );
198198
}
199+
200+
/**
201+
* Generate terms for the Product Category taxonomy.
202+
*
203+
* @param array $args Arguments specified.
204+
* @param array $assoc_args Associative arguments specified.
205+
*/
206+
public static function terms( $args, $assoc_args ) {
207+
list( $taxonomy, $amount ) = $args;
208+
$amount = absint( $amount );
209+
210+
$time_start = microtime( true );
211+
212+
$progress = \WP_CLI\Utils\make_progress_bar( 'Generating terms', $amount );
213+
214+
add_action(
215+
'smoothgenerator_term_generated',
216+
function () use ( $progress ) {
217+
$progress->tick();
218+
}
219+
);
220+
221+
$result = Generator\Term::batch( $amount, $taxonomy, $assoc_args );
222+
223+
if ( is_wp_error( $result ) ) {
224+
WP_CLI::error( $result );
225+
}
226+
227+
$progress->finish();
228+
229+
$generated = count( $result );
230+
$time_end = microtime( true );
231+
$execution_time = round( ( $time_end - $time_start ), 2 );
232+
$display_time = $execution_time < 60 ? $execution_time . ' seconds' : human_time_diff( $time_start, $time_end );
233+
234+
WP_CLI::success( $generated . ' terms generated in ' . $display_time );
235+
}
199236
}
200237

201238
WP_CLI::add_command( 'wc generate products', array( 'WC\SmoothGenerator\CLI', 'products' ), array(
@@ -274,3 +311,38 @@ public static function coupons( $args, $assoc_args ) {
274311
),
275312
),
276313
) );
314+
315+
WP_CLI::add_command( 'wc generate terms', array( 'WC\SmoothGenerator\CLI', 'terms' ), array(
316+
'shortdesc' => 'Generate product categories.',
317+
'synopsis' => array(
318+
array(
319+
'name' => 'taxonomy',
320+
'type' => 'positional',
321+
'description' => 'The taxonomy to generate the terms for.',
322+
'options' => array( 'product_cat', 'product_tag' ),
323+
),
324+
array(
325+
'name' => 'amount',
326+
'type' => 'positional',
327+
'description' => 'The number of terms to generate. Max value 100.',
328+
'optional' => true,
329+
'default' => 10,
330+
),
331+
array(
332+
'name' => 'max-depth',
333+
'type' => 'assoc',
334+
'description' => 'The maximum number of hierarchy levels for the terms. A value of 1 means all categories will be top-level. Max value 5. Only applies to taxonomies that are hierarchical.',
335+
'optional' => true,
336+
'options' => array( 1, 2, 3, 4, 5 ),
337+
'default' => 1,
338+
),
339+
array(
340+
'name' => 'parent',
341+
'type' => 'assoc',
342+
'description' => 'Specify an existing term ID as the parent for the new terms. Only applies to taxonomies that are hierarchical.',
343+
'optional' => true,
344+
'default' => 0,
345+
),
346+
),
347+
'longdesc' => "## EXAMPLES\n\nwc generate terms product_tag 10\n\nwc generate terms product_cat 50 --max_depth=3",
348+
) );

includes/Generator/Term.php

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
<?php
2+
/**
3+
* Generate taxonomy terms.
4+
*
5+
* @package SmoothGenerator\Classes
6+
*/
7+
8+
namespace WC\SmoothGenerator\Generator;
9+
10+
/**
11+
* Customer data generator.
12+
*/
13+
class Term extends Generator {
14+
/**
15+
* Init faker library.
16+
*/
17+
protected static function init_faker() {
18+
parent::init_faker();
19+
self::$faker->addProvider( new \Bezhanov\Faker\Provider\Commerce( self::$faker ) );
20+
}
21+
22+
/**
23+
* Create a new taxonomy term.
24+
*
25+
* @param bool $save Whether to save the new term to the database.
26+
* @param string $taxonomy The taxonomy slug.
27+
* @param int $parent ID of parent term.
28+
*
29+
* @return \WP_Error|\WP_Term
30+
*/
31+
public static function generate( $save = true, string $taxonomy = 'product_cat', int $parent = 0 ) {
32+
$taxonomy_obj = get_taxonomy( $taxonomy );
33+
if ( ! $taxonomy_obj ) {
34+
return new \WP_Error(
35+
'smoothgenerator_invalid_taxonomy',
36+
'The specified taxonomy is invalid.'
37+
);
38+
}
39+
40+
if ( 0 !== $parent && true !== $taxonomy_obj->hierarchical ) {
41+
return new \WP_Error(
42+
'smoothgenerator_invalid_term_hierarchy',
43+
'The specified taxonomy does not support parent terms.'
44+
);
45+
}
46+
47+
self::init_faker();
48+
49+
if ( $taxonomy_obj->hierarchical ) {
50+
$term_name = ucwords( self::$faker->department( 3 ) );
51+
} else {
52+
$term_name = self::random_weighted_element( array(
53+
self::$faker->lastName() => 45,
54+
self::$faker->colorName() => 35,
55+
self::$faker->words( 3, true ) => 20,
56+
) );
57+
$term_name = strtolower( $term_name );
58+
}
59+
60+
$term_args = array(
61+
'description' => self::$faker->realTextBetween( 20, wp_rand( 20, 300 ), 4 ),
62+
);
63+
if ( 0 !== $parent ) {
64+
$term_args['parent'] = $parent;
65+
}
66+
67+
$result = wp_insert_term( $term_name, $taxonomy, $term_args );
68+
69+
if ( is_wp_error( $result ) ) {
70+
return $result;
71+
}
72+
73+
$term = get_term( $result['term_id'] );
74+
75+
/**
76+
* Action: Term generator returned a new term.
77+
*
78+
* @since TBD
79+
*
80+
* @param \WP_Term $term
81+
*/
82+
do_action( 'smoothgenerator_term_generated', $term );
83+
84+
return $term;
85+
}
86+
87+
/**
88+
* Create multiple terms for a taxonomy.
89+
*
90+
* @param int $amount The number of terms to create.
91+
* @param string $taxonomy The taxonomy to assign the terms to.
92+
* @param array $args Additional args for term creation.
93+
*
94+
* @return int[]|\WP_Error
95+
*/
96+
public static function batch( $amount, $taxonomy, array $args = array() ) {
97+
$amount = filter_var(
98+
$amount,
99+
FILTER_VALIDATE_INT,
100+
array(
101+
'min_range' => 1,
102+
'max_range' => 100,
103+
)
104+
);
105+
106+
if ( false === $amount ) {
107+
return new \WP_Error(
108+
'smoothgenerator_term_batch_invalid_amount',
109+
'Amount must be a number between 1 and 100.'
110+
);
111+
}
112+
113+
$taxonomy_obj = get_taxonomy( $taxonomy );
114+
if ( ! $taxonomy_obj ) {
115+
return new \WP_Error(
116+
'smoothgenerator_term_batch_invalid_taxonomy',
117+
'The specified taxonomy is invalid.'
118+
);
119+
}
120+
121+
if ( true === $taxonomy_obj->hierarchical ) {
122+
return self::batch_hierarchical( $amount, $taxonomy, $args );
123+
}
124+
125+
$term_ids = array();
126+
127+
for ( $i = 1; $i <= $amount; $i ++ ) {
128+
$term = self::generate( true, $taxonomy );
129+
if ( is_wp_error( $term ) ) {
130+
if ( 'term_exists' === $term->get_error_code() ) {
131+
$i --; // Try again.
132+
continue;
133+
}
134+
135+
return $term;
136+
}
137+
$term_ids[] = $term->term_id;
138+
}
139+
140+
return $term_ids;
141+
}
142+
143+
/**
144+
* Create multiple terms for a hierarchical taxonomy.
145+
*
146+
* @param int $amount The number of terms to create.
147+
* @param string $taxonomy The taxonomy to assign the terms to.
148+
* @param array $args Additional args for term creation.
149+
* @type int $max_depth The maximum level of hierarchy.
150+
* @type int $parent ID of a term to be the parent of the generated terms.
151+
*
152+
* @return int[]|\WP_Error
153+
*/
154+
protected static function batch_hierarchical( int $amount, string $taxonomy, array $args = array() ) {
155+
$defaults = array(
156+
'max-depth' => 1,
157+
'parent' => 0,
158+
);
159+
160+
list( 'max-depth' => $max_depth, 'parent' => $parent ) = filter_var_array(
161+
wp_parse_args( $args, $defaults ),
162+
array(
163+
'max-depth' => array(
164+
'filter' => FILTER_VALIDATE_INT,
165+
'options' => array(
166+
'min_range' => 1,
167+
'max_range' => 5,
168+
),
169+
),
170+
'parent' => FILTER_VALIDATE_INT,
171+
)
172+
);
173+
174+
if ( false === $max_depth ) {
175+
return new \WP_Error(
176+
'smoothgenerator_term_batch_invalid_max_depth',
177+
'Max depth must be a number between 1 and 5.'
178+
);
179+
}
180+
if ( false === $parent ) {
181+
return new \WP_Error(
182+
'smoothgenerator_term_batch_invalid_parent',
183+
'Parent must be the ID number of an existing term.'
184+
);
185+
}
186+
187+
$term_ids = array();
188+
189+
self::init_faker();
190+
191+
if ( $parent || 1 === $max_depth ) {
192+
// All terms will be in the same hierarchy level.
193+
for ( $i = 1; $i <= $amount; $i ++ ) {
194+
$term = self::generate( true, $taxonomy, $parent );
195+
if ( is_wp_error( $term ) ) {
196+
if ( 'term_exists' === $term->get_error_code() ) {
197+
$i --; // Try again.
198+
continue;
199+
}
200+
201+
return $term;
202+
}
203+
$term_ids[] = $term->term_id;
204+
}
205+
} else {
206+
$remaining = $amount;
207+
$term_max = 1;
208+
if ( $amount > 2 ) {
209+
$term_max = floor( log( $amount ) );
210+
}
211+
$levels = array_fill( 1, $max_depth, array() );
212+
213+
for ( $i = 1; $i <= $max_depth; $i ++ ) {
214+
if ( 1 === $i ) {
215+
// Always use the full term max for the top level of the hierarchy.
216+
for ( $j = 1; $j <= $term_max && $remaining > 0; $j ++ ) {
217+
$term = self::generate( true, $taxonomy );
218+
if ( is_wp_error( $term ) ) {
219+
if ( 'term_exists' === $term->get_error_code() ) {
220+
$j --; // Try again.
221+
continue;
222+
}
223+
224+
return $term;
225+
}
226+
$term_ids[] = $term->term_id;
227+
$levels[ $i ][] = $term->term_id;
228+
$remaining --;
229+
}
230+
} else {
231+
// Subsequent hierarchy levels.
232+
foreach ( $levels[ $i - 1 ] as $term_id ) {
233+
$tcount = wp_rand( 0, $term_max );
234+
235+
for ( $j = 1; $j <= $tcount && $remaining > 0; $j ++ ) {
236+
$term = self::generate( true, $taxonomy, $term_id );
237+
if ( is_wp_error( $term ) ) {
238+
if ( 'term_exists' === $term->get_error_code() ) {
239+
$j --; // Try again.
240+
continue;
241+
}
242+
243+
return $term;
244+
}
245+
$term_ids[] = $term->term_id;
246+
$levels[ $i ][] = $term->term_id;
247+
$remaining --;
248+
}
249+
}
250+
}
251+
if ( $i === $max_depth && $remaining > 0 ) {
252+
// If we haven't generated enough yet, start back at the top level of the hierarchy.
253+
$i = 0;
254+
}
255+
}
256+
}
257+
258+
return $term_ids;
259+
}
260+
}

0 commit comments

Comments
 (0)