diff --git a/src/Exceptions/TaxonomyRegistrationException.php b/src/Exceptions/TaxonomyRegistrationException.php new file mode 100644 index 0000000..eab19c0 --- /dev/null +++ b/src/Exceptions/TaxonomyRegistrationException.php @@ -0,0 +1,8 @@ +get('taxonomies.register'); + + foreach ($taxonomiesToRegister as $taxonomy) { + $taxonomy::register(); + } + } +} diff --git a/src/Term.php b/src/Term.php new file mode 100644 index 0000000..1c20e45 --- /dev/null +++ b/src/Term.php @@ -0,0 +1,156 @@ +__macroableCall($name, $arguments); + } + + return parent::__call($name, $arguments); + } + + public static function __callStatic($name, $arguments) + { + if (static::hasMacro($name)) { + return static::__macroableCallStatic($name, $arguments); + } + + trigger_error('Call to undefined method '.__CLASS__.'::'.$name.'()', E_USER_ERROR); + } + + /** + * Return the key used to register the taxonomy with WordPress + * First parameter of the `register_taxonomy` function: + * https://developer.wordpress.org/reference/functions/register_taxonomy/ + * + * @return string + */ + public static function getTaxonomyType() + { + return null; + } + + /** + * Return the object type which use this taxonomy. + * Second parameter of the `register_taxonomy` function: + * https://developer.wordpress.org/reference/functions/register_taxonomy/ + * + * @return array|null + */ + public static function getTaxonomyObjectTypes() + { + return ['post']; + } + + /** + * Return the config to use to register the taxonomy with WordPress + * Third parameter of the `register_taxonomy` function: + * https://developer.wordpress.org/reference/functions/register_taxonomy/ + * + * @return array|null + */ + protected static function getTaxonomyConfig() + { + return null; + } + + /** + * Register this PostType with WordPress + * + * @return void + */ + public static function register() + { + $taxonomyType = static::getTaxonomyType(); + $taxonomyObjectTypes = static::getTaxonomyObjectTypes(); + $config = static::getTaxonomyConfig(); + + if (empty($taxonomyType)) { + throw new TaxonomyRegistrationException('Taxonomy type not set'); + } + + if (empty($taxonomyObjectTypes)) { + throw new TaxonomyRegistrationException('Taxonomy object type not set'); + } + + if (empty($config)) { + throw new TaxonomyRegistrationException('Config not set'); + } + + register_taxonomy($taxonomyType, $taxonomyObjectTypes, $config); + } + + /** + * Get all terms of this taxonomy + * + * @param string $orderby Field(s) to order terms by (defaults to term_order) + * @param string $order Whether to order terms in ascending or descending order (defaults to ASC) + * @return Illuminate\Support\Collection + */ + public static function all($orderby = 'term_order', $order = 'ASC') + { + $order = strtoupper($order); + + $args = [ + 'orderby' => $orderby, + 'order' => $order, + ]; + + return static::query($args); + } + + + /** + * Convenience function that takes a standard set of WP_Term_Query arguments but mixes it with + * arguments that mean we're selecting the right taxonomy type + * + * @param array $args standard WP_Term_Query array + * @return Illuminate\Support\Collection + */ + public static function query($args = null) + { + $args = is_array($args) ? $args : []; + + // Set the correct post type + $args = array_merge($args, ['taxonomy' => static::getTaxonomyType()]); + + return static::terms($args); + } + + /** + * Raw query function that uses the arguments provided to make a call to Timber::get_terms + * and casts the returning data in instances of ourself. + * + * @param array $args standard WP_Query array + * @return Illuminate\Support\Collection + */ + private static function terms($args = null) + { + return collect(Timber::get_terms($args, [], get_called_class())); + } +} diff --git a/tests/Unit/Providers/CustomTaxonomyServiceProviderTest.php b/tests/Unit/Providers/CustomTaxonomyServiceProviderTest.php new file mode 100644 index 0000000..8fb61e1 --- /dev/null +++ b/tests/Unit/Providers/CustomTaxonomyServiceProviderTest.php @@ -0,0 +1,76 @@ +set('taxonomies.register', [ + CustomTaxonomy1::class, + CustomTaxonomy2::class, + ]); + + Functions\expect('register_taxonomy') + ->times(2); + + $provider = new CustomTaxonomyServiceProvider($app); + $provider->boot($config); + } +} + +class CustomTaxonomy1 extends Term +{ + public static function getTaxonomyType() + { + return 'custom_taxonomy_1'; + } + + public static function getTaxonomyObjectTypes() + { + return ['post']; + } + + protected static function getTaxonomyConfig() + { + return [ + 'not' => 'empty', + ]; + } +} + +class CustomTaxonomy2 extends Term +{ + public static function getTaxonomyType() + { + return 'custom_taxonomy_1'; + } + + public static function getTaxonomyObjectTypes() + { + return ['post']; + } + + protected static function getTaxonomyConfig() + { + return [ + 'not' => 'empty', + ]; + } +} diff --git a/tests/Unit/TermTest.php b/tests/Unit/TermTest.php new file mode 100644 index 0000000..7c479af --- /dev/null +++ b/tests/Unit/TermTest.php @@ -0,0 +1,282 @@ +once() + ->with(RegisterableTaxonomyType::getTaxonomyType(), RegisterableTaxonomyType::getTaxonomyObjectTypes(), RegisterableTaxonomyType::getPrivateConfig()); + + RegisterableTaxonomyType::register(); + } + + /** + * @test + * @expectedException Rareloop\Lumberjack\Exceptions\TaxonomyRegistrationException + */ + public function register_function_throws_exception_if_taxonomy_type_is_not_provided() + { + UnregisterableTaxonomyWithoutTaxonomyType::register(); + } + + /** + * @test + * @expectedException Rareloop\Lumberjack\Exceptions\TaxonomyRegistrationException + */ + public function register_function_throws_exception_if_config_is_not_provided() + { + UnregisterableTaxonomyWithoutConfig::register(); + } + + /** + * @test + */ + public function query_defaults_to_current_taxonomy_type() + { + $args = [ + 'show_admin_column' => true, + ]; + $maybe_args = []; + + $timber = Mockery::mock('alias:' . Timber::class); + $timber->shouldReceive('get_terms')->withArgs([ + array_merge($args, [ + 'taxonomy' => Term::getTaxonomyType(), + ]), + $maybe_args, + Term::class, + ])->once(); + + $terms = Term::query($args); + + $this->assertInstanceOf(Collection::class, $terms); + } + + /** + * @test + */ + public function query_ignores_passed_in_taxonomy() + { + $args = [ + 'taxonomy' => 'something-else', + 'show_admin_column' => true, + ]; + $maybe_args = []; + + $timber = Mockery::mock('alias:' . Timber::class); + $timber->shouldReceive('get_terms')->withArgs([ + array_merge($args, [ + 'taxonomy' => Term::getTaxonomyType(), + 'show_admin_column' => true, + ]), + $maybe_args, + Term::class, + ])->once(); + + $terms = Term::query($args); + + $this->assertInstanceOf(Collection::class, $terms); + } + + /** + * @test + */ + public function term_subclass_query_has_correct_taxonomy_type() + { + $args = [ + 'show_admin_column' => true, + ]; + $maybe_args = []; + + $timber = Mockery::mock('alias:' . Timber::class); + $timber->shouldReceive('get_terms')->withArgs([ + Mockery::subset([ + 'taxonomy' => RegisterableTaxonomyType::getTaxonomyType(), + ]), + $maybe_args, + RegisterableTaxonomyType::class, + ])->once(); + + $terms = RegisterableTaxonomyType::query($args); + + $this->assertInstanceOf(Collection::class, $terms); + } + + /** + * @test + */ + public function all_defaults_to_ordered_by_term_order_ascending() + { + $maybe_args = []; + $timber = Mockery::mock('alias:' . Timber::class); + $timber->shouldReceive('get_terms')->withArgs([ + Mockery::subset([ + 'orderby' => 'term_order', + 'order' => 'ASC', + ]), + $maybe_args, + Term::class, + ])->once(); + + $terms = Term::all(); + + $this->assertInstanceOf(Collection::class, $terms); + } + + + /** + * @test + */ + public function all_can_have_order_set() + { + $maybe_args = []; + $timber = Mockery::mock('alias:' . Timber::class); + $timber->shouldReceive('get_terms')->withArgs([ + Mockery::subset([ + 'orderby' => 'slug', + 'order' => 'DESC', + ]), + $maybe_args, + Term::class, + ])->once(); + + $terms = Term::all('slug', 'DESC'); + + $this->assertInstanceOf(Collection::class, $terms); + } + + /** + * @test + */ + public function can_extend_term_behaviour_with_macros() + { + Term::macro('testFunctionAddedByMacro', function () { + return 'abc123'; + }); + + $term = new Term(false, '', true); + + $this->assertSame('abc123', $term->testFunctionAddedByMacro()); + $this->assertSame('abc123', Term::testFunctionAddedByMacro()); + } + + /** + * @test + */ + public function macros_set_correct_this_context_on_instances() + { + Term::macro('testFunctionAddedByMacro', function () { + return $this->dummyData(); + }); + + $term = new Term(false, '', true); + $term->dummyData = 'abc123'; + + $this->assertSame('abc123', $term->testFunctionAddedByMacro()); + } + + /** + * @test + */ + public function can_extend_term_behaviour_with_mixin() + { + Term::mixin(new TermMixin); + + $term = new Term(false, '', true); + + $this->assertSame('abc123', $term->testFunctionAddedByMixin()); + } +} + +class TermMixin +{ + function testFunctionAddedByMixin() + { + return function() { + return 'abc123'; + }; + } +} + +class RegisterableTaxonomyType extends Term +{ + public static function getTaxonomyType() : string + { + return 'registerable_taxonomy_type'; + } + + public static function getTaxonomyObjectTypes() : array + { + return ['post']; + } + + protected static function getTaxonomyConfig() : array + { + return [ + 'hierarchical' => true, + 'labels' => [ + 'name' => 'Tags', + 'singular_name' => 'Tag' + ], + 'show_ui' => true, + 'show_admin_column' => true, + 'query_var' => true, + 'rewrite' => [ + 'slug' => 'the-tags' + ], + ]; + } + + public static function getPrivateConfig() + { + return self::getTaxonomyConfig(); + } +} + +class UnregisterableTaxonomyWithoutTaxonomyType extends Term +{ + protected static function getTaxonomyConfig() : array + { + return [ + 'labels' => [ + 'name' => 'Groups', + 'singular_name' => 'Group' + ], + 'public' => true, + 'has_archive' => false, + 'supports' => ['title', 'revisions'], + 'menu_icon' => 'dashicons-groups', + 'rewrite' => [ + 'slug' => 'group', + ], + ]; + } +} + +class UnregisterableTaxonomyWithoutConfig extends Term +{ + public static function getTaxonomyType() : string + { + return 'taxonomy_type'; + } +}