From 4134412fd3d546767091fd8545f0c5a4b9f659c0 Mon Sep 17 00:00:00 2001 From: David Mignot <197418+idflood@users.noreply.github.com> Date: Fri, 2 Oct 2020 09:00:01 +0200 Subject: [PATCH 1/2] Add custom taxonomy support --- .../TaxonomyRegistrationException.php | 8 + .../CustomTaxonomyServiceProvider.php | 15 + src/Term.php | 156 ++++++++++ .../CustomTaxonomyServiceProviderTest.php | 76 +++++ tests/Unit/TermTest.php | 282 ++++++++++++++++++ 5 files changed, 537 insertions(+) create mode 100644 src/Exceptions/TaxonomyRegistrationException.php create mode 100644 src/Providers/CustomTaxonomyServiceProvider.php create mode 100644 src/Term.php create mode 100644 tests/Unit/Providers/CustomTaxonomyServiceProviderTest.php create mode 100644 tests/Unit/TermTest.php 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'; + } +} From 92c93823d30a530001c9369164577d9ef94fc0ae Mon Sep 17 00:00:00 2001 From: David Mignot <197418+idflood@users.noreply.github.com> Date: Fri, 2 Oct 2020 12:00:51 +0200 Subject: [PATCH 2/2] Fix indentation --- src/Providers/CustomTaxonomyServiceProvider.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Providers/CustomTaxonomyServiceProvider.php b/src/Providers/CustomTaxonomyServiceProvider.php index 40ac9e8..2a09aba 100644 --- a/src/Providers/CustomTaxonomyServiceProvider.php +++ b/src/Providers/CustomTaxonomyServiceProvider.php @@ -4,12 +4,14 @@ use Rareloop\Lumberjack\Config; -class CustomTaxonomyServiceProvider extends ServiceProvider { - public function boot(Config $config) { - $taxonomiesToRegister = $config->get('taxonomies.register'); +class CustomTaxonomyServiceProvider extends ServiceProvider +{ + public function boot(Config $config) + { + $taxonomiesToRegister = $config->get('taxonomies.register'); - foreach ($taxonomiesToRegister as $taxonomy) { - $taxonomy::register(); + foreach ($taxonomiesToRegister as $taxonomy) { + $taxonomy::register(); + } } - } }