diff --git a/src/Columns.php b/src/Columns.php index 14ac23d..97d523c 100644 --- a/src/Columns.php +++ b/src/Columns.php @@ -58,8 +58,9 @@ class Columns public $sortable = []; /** - * Set the all columns - * @param array $columns an array of all the columns to replace + * Set the all columns. + * + * @param array $columns an array of all the columns to replace. */ public function set($columns) { @@ -67,9 +68,12 @@ public function set($columns) } /** - * Add a new column - * @param string $column the slug of the column - * @param string $label the label for the column + * Add a new column. + * + * @param string $column the slug of the column. + * @param string $label the label for the column. + * + * @return PostType\Columns */ public function add($columns, $label = null) { @@ -90,8 +94,11 @@ public function add($columns, $label = null) } /** - * Add a column to hide - * @param string $column the slug of the column to hdie + * Add a column to hide. + * + * @param string $column the slug of the column to hide. + * + * @return PostType\Columns */ public function hide($columns) { @@ -107,9 +114,12 @@ public function hide($columns) } /** - * Set a custom callback to populate a column - * @param string $column the column slug - * @param mixed $callback callback function + * Set a custom callback to populate a column. + * + * @param string $column the column slug. + * @param mixed $callback callback function. + * + * @return PostType\Columns */ public function populate($column, $callback) { @@ -119,8 +129,11 @@ public function populate($column, $callback) } /** - * Define the postion for a columns + * Define the postion for a columns. + * * @param string $columns an array of columns + * + * @return PostType\Columns */ public function order($columns) { @@ -132,10 +145,13 @@ public function order($columns) } /** - * Set columns that are sortable + * Set columns that are sortable. + * * @param string $column the slug of the column * @param string $meta_value the meta_value to orderby * @param boolean $is_num whether to order by string/number + * + * @return PostType\Columns */ public function sortable($sortable) { @@ -148,7 +164,10 @@ public function sortable($sortable) /** * Check if an orderby field is a custom sort option. - * @param string $orderby the orderby value from query params + * + * @param string $orderby the orderby value from query params. + * + * @return boolean */ public function isSortable($orderby) { @@ -170,7 +189,10 @@ public function isSortable($orderby) /** * Get meta key for an orderby. - * @param string $orderby the orderby value from query params + * + * @param string $orderby the orderby value from query params + * + * @return mixed */ public function sortableMeta($orderby) { @@ -191,46 +213,48 @@ public function sortableMeta($orderby) } /** - * Modify the columns for the object + * Modify the columns for the object. + * * @param array $columns WordPress default columns - * @return array The modified columns + * + * @return array */ public function modifyColumns($columns) { - // if user defined set columns, return those + // If user defined set columns, return those. if (!empty($this->items)) { return $this->items; } - // add additional columns + // Add additional columns. if (!empty($this->add)) { foreach ($this->add as $key => $label) { $columns[$key] = $label; } } - // unset hidden columns + // Unset hidden columns. if (!empty($this->hide)) { foreach ($this->hide as $key) { unset($columns[$key]); } } - // if user has made added custom columns + // If user has made added custom columns. if (!empty($this->positions)) { foreach ($this->positions as $key => $position) { - // find index of the element in the array + // Find index of the element in the array. $index = array_search($key, array_keys($columns)); - // retrieve the element in the array of columns + // Retrieve the element in the array of columns. $item = array_slice($columns, $index, 1); - // remove item from the array + // Remove item from the array. unset($columns[$key]); - // split columns array into two at the desired position + // Split columns array into two at the desired position. $start = array_slice($columns, 0, $position, true); $end = array_slice($columns, $position, count($columns) - 1, true); - // insert column into position + // Insert column into position. $columns = $start + $item + $end; } } diff --git a/src/Contracts/PostType.php b/src/Contracts/PostType.php new file mode 100644 index 0000000..998d6ee --- /dev/null +++ b/src/Contracts/PostType.php @@ -0,0 +1,74 @@ +names($names); - // assign custom options to the PostType + // Assign options to the PostType. $this->options($options); - // assign labels to the PostType + // Assign labels to the PostType. $this->labels($labels); } /** - * Set the names for the PostType - * @param mixed $names A string for the name, or an array of names - * @return $this + * Set the names for the PostType. + * + * @param mixed $names A string for the name, or an array of names. + * + * @return PostTypes\PostType */ public function names($names) { - // only the post type name is passed - if (is_string($names)) { - $names = ['name' => $names]; - } - - // set the names array - $this->names = $names; - - // create names for the PostType - $this->createNames(); + // If only the post type name is passed. + $this->names = is_string($names) ? ['name' => $names] : $names; return $this; } /** - * Set the options for the PostType - * @param array $options An array of options for the PostType - * @return $this + * Set the options for the PostType. + * + * @param array $options An array of options for the PostType. + * + * @return PostTypes\PostType */ public function options(array $options) { @@ -134,9 +146,11 @@ public function options(array $options) } /** - * Set the labels for the PostType - * @param array $labels An array of labels for the PostType - * @return $this + * Set the labels for the PostType. + * + * @param array $labels An array of labels for the PostType. + * + * @return PostTypes\PostType */ public function labels(array $labels) { @@ -146,25 +160,25 @@ public function labels(array $labels) } /** - * Add a Taxonomy to the PostType - * @param mixed $taxonomies The Taxonomy name(s) to add - * @return $this + * Add a Taxonomy to the PostType. + * + * @param mixed $taxonomies The Taxonomy name(s) to add. + * + * @return PostTypes\PostType */ public function taxonomy($taxonomies) { - $taxonomies = is_string($taxonomies) ? [$taxonomies] : $taxonomies; - - foreach ($taxonomies as $taxonomy) { - $this->taxonomies[] = $taxonomy; - } + $this->taxonomies = is_string($taxonomies) ? [$taxonomies] : $taxonomies; return $this; } /** - * Add filters to the PostType - * @param array $filters An array of Taxonomy filters - * @return $this + * Add filters to the PostType. + * + * @param array $filters An array of Taxonomy filters. + * + * @return PostTypes\PostType */ public function filters(array $filters) { @@ -174,9 +188,11 @@ public function filters(array $filters) } /** - * Set the menu icon for the PostType - * @param string $icon A dashicon class for the menu icon - * @return $this + * Set the menu icon for the PostType. + * + * @param string $icon A dashicon class for the menu icon. + * + * @return PostTypes\PostType */ public function icon($icon) { @@ -186,9 +202,12 @@ public function icon($icon) } /** - * Flush rewrite rules + * Flush rewrite rules. + * * @link https://codex.wordpress.org/Function_Reference/flush_rewrite_rules + * * @param boolean $hard + * * @return void */ public function flush($hard = true) @@ -197,7 +216,8 @@ public function flush($hard = true) } /** - * Get the Column Manager for the PostType + * Get the Column Manager for the PostType. + * * @return Columns */ public function columns() @@ -210,334 +230,12 @@ public function columns() } /** - * Register the PostType to WordPress - * @return void - */ - public function register() - { - // register the PostType - if (!post_type_exists($this->name)) { - add_action('init', [$this, 'registerPostType']); - } else { - add_filter('register_post_type_args', [$this, 'modifyPostType'], 10, 2); - } - - // register Taxonomies to the PostType - add_action('init', [$this, 'registerTaxonomies']); - - // modify filters on the admin edit screen - add_action('restrict_manage_posts', [$this, 'modifyFilters']); - - if (isset($this->columns)) { - // modify the admin edit columns. - add_filter("manage_{$this->name}_posts_columns", [$this, 'modifyColumns'], 10, 1); - - // populate custom columns - add_filter("manage_{$this->name}_posts_custom_column", [$this, 'populateColumns'], 10, 2); - - // run filter to make columns sortable. - add_filter('manage_edit-'.$this->name.'_sortable_columns', [$this, 'setSortableColumns']); - - // run action that sorts columns on request. - add_action('pre_get_posts', [$this, 'sortSortableColumns']); - } - } - - /** - * Register the PostType - * @return void - */ - public function registerPostType() - { - // create options for the PostType - $options = $this->createOptions(); - - // check that the post type doesn't already exist - if (!post_type_exists($this->name)) { - // register the post type - register_post_type($this->name, $options); - } - } - - /** - * Modify the existing Post Type. + * Register the PostType to WordPress. * - * @return array - */ - public function modifyPostType(array $args, string $posttype) - { - if ($posttype !== $this->name) { - return $args; - } - - // create options for the PostType - $options = $this->createOptions(); - - $args = array_replace_recursive($args, $options); - - return $args; - } - - /** - * Create the required names for the PostType * @return void */ - public function createNames() - { - // names required for the PostType - $required = [ - 'name', - 'singular', - 'plural', - 'slug', - ]; - - foreach ($required as $key) { - // if the name is set, assign it - if (isset($this->names[$key])) { - $this->$key = $this->names[$key]; - continue; - } - - // if the key is not set and is singular or plural - if (in_array($key, ['singular', 'plural'])) { - // create a human friendly name - $name = ucwords(strtolower(str_replace(['-', '_'], ' ', $this->names['name']))); - } - - if ($key === 'slug') { - // create a slug friendly name - $name = strtolower(str_replace([' ', '_'], '-', $this->names['name'])); - } - - // if is plural or slug, append an 's' - if (in_array($key, ['plural', 'slug'])) { - if (substr($name, strlen($name) - 1, 1) == "y") { - $name = substr($name, 0, strlen($name) - 1) . "ies"; - } else { - $name .= 's'; - } - } - - // asign the name to the PostType property - $this->$key = $name; - } - } - - /** - * Create options for PostType - * @return array Options to pass to register_post_type - */ - public function createOptions() - { - // default options - $options = [ - 'public' => true, - 'rewrite' => [ - 'slug' => $this->slug - ] - ]; - - // replace defaults with the options passed - $options = array_replace_recursive($options, $this->options); - - // create and set labels - if (!isset($options['labels'])) { - $options['labels'] = $this->createLabels(); - } - - // set the menu icon - if (!isset($options['menu_icon']) && isset($this->icon)) { - $options['menu_icon'] = $this->icon; - } - - return $options; - } - - /** - * Create the labels for the PostType - * @return array - */ - public function createLabels() - { - // default labels - $labels = [ - 'name' => $this->plural, - 'singular_name' => $this->singular, - 'menu_name' => $this->plural, - 'all_items' => $this->plural, - 'add_new' => "Add New", - 'add_new_item' => "Add New {$this->singular}", - 'edit_item' => "Edit {$this->singular}", - 'new_item' => "New {$this->singular}", - 'view_item' => "View {$this->singular}", - 'search_items' => "Search {$this->plural}", - 'not_found' => "No {$this->plural} found", - 'not_found_in_trash' => "No {$this->plural} found in Trash", - 'parent_item_colon' => "Parent {$this->singular}:", - ]; - - return array_replace_recursive($labels, $this->labels); - } - - /** - * Register Taxonomies to the PostType - * @return void - */ - public function registerTaxonomies() - { - if (!empty($this->taxonomies)) { - foreach ($this->taxonomies as $taxonomy) { - register_taxonomy_for_object_type($taxonomy, $this->name); - } - } - } - - /** - * Modify and display filters on the admin edit screen - * @param string $posttype The current screen post type - * @return void - */ - public function modifyFilters($posttype) - { - // first check we are working with the this PostType - if ($posttype === $this->name) { - // calculate what filters to add - $filters = $this->getFilters(); - - foreach ($filters as $taxonomy) { - // if the taxonomy doesn't exist, ignore it - if (!taxonomy_exists($taxonomy)) { - continue; - } - - // If the taxonomy is not registered to the post type, continue. - if (!is_object_in_taxonomy($this->name, $taxonomy)) { - continue; - } - - // get the taxonomy object - $tax = get_taxonomy($taxonomy); - - // start the html for the filter dropdown - $selected = null; - - if (isset($_GET[$taxonomy])) { - $selected = sanitize_title($_GET[$taxonomy]); - } - - $dropdown_args = [ - 'name' => $taxonomy, - 'value_field' => 'slug', - 'taxonomy' => $tax->name, - 'show_option_all' => $tax->labels->all_items, - 'hierarchical' => $tax->hierarchical, - 'selected' => $selected, - 'orderby' => 'name', - 'hide_empty' => 0, - 'show_count' => 0, - ]; - - // Output screen reader label. - echo ''; - - // Output dropdown for taxonomy. - wp_dropdown_categories($dropdown_args); - } - } - } - - /** - * Calculate the filters for the PostType - * @return array - */ - public function getFilters() - { - // default filters are empty - $filters = []; - - // if custom filters have been set, use them - if (!is_null($this->filters)) { - return $this->filters; - } - - // if no custom filters have been set, and there are - // Taxonomies assigned to the PostType - if (is_null($this->filters) && !empty($this->taxonomies)) { - // create filters for each taxonomy assigned to the PostType - return $this->taxonomies; - } - - return $filters; - } - - /** - * Modify the columns for the PostType - * @param array $columns Default WordPress columns - * @return array The modified columns - */ - public function modifyColumns($columns) - { - $columns = $this->columns->modifyColumns($columns); - - return $columns; - } - - /** - * Populate custom columns for the PostType - * @param string $column The column slug - * @param int $post_id The post ID - */ - public function populateColumns($column, $post_id) - { - if (isset($this->columns->populate[$column])) { - call_user_func_array($this->columns()->populate[$column], [$column, $post_id]); - } - } - - /** - * Make custom columns sortable - * @param array $columns Default WordPress sortable columns - */ - public function setSortableColumns($columns) - { - if (!empty($this->columns()->sortable)) { - $columns = array_merge($columns, $this->columns()->sortable); - } - - return $columns; - } - - /** - * Set query to sort custom columns - * @param WP_Query $query - */ - public function sortSortableColumns($query) + public function register() { - // don't modify the query if we're not in the post type admin - if (!is_admin() || $query->get('post_type') !== $this->name) { - return; - } - - $orderby = $query->get('orderby'); - - // if the sorting a custom column - if ($this->columns()->isSortable($orderby)) { - // get the custom column options - $meta = $this->columns()->sortableMeta($orderby); - - // determine type of ordering - if (is_string($meta) or !$meta[1]) { - $meta_key = $meta; - $meta_value = 'meta_value'; - } else { - $meta_key = $meta[0]; - $meta_value = 'meta_value_num'; - } - - // set the custom order - $query->set('meta_key', $meta_key); - $query->set('orderby', $meta_value); - } + (new PostTypeRegistrar($this))->register(); } } diff --git a/src/Registrars/PostTypeRegistrar.php b/src/Registrars/PostTypeRegistrar.php new file mode 100644 index 0000000..b33e75f --- /dev/null +++ b/src/Registrars/PostTypeRegistrar.php @@ -0,0 +1,374 @@ +posttype = $posttype; + } + + /** + * Register the PostType to WordPress. + * + * @return void + */ + public function register() + { + // Create the names required to register the post type. + $this->createNames(); + + // Register the PostType. + if (!post_type_exists($this->posttype->name)) { + add_action('init', [$this, 'registerPostType']); + } else { + add_filter('register_post_type_args', [$this, 'modifyPostType'], 10, 2); + } + + // Register Taxonomies to the PostType. + add_action('init', [$this, 'registerTaxonomies']); + + // Modify filters on the admin edit screen. + add_action('restrict_manage_posts', [$this, 'modifyFilters']); + + if (isset($this->posttype->columns)) { + // Modify the admin edit columns. + add_filter("manage_{$this->posttype->name}_posts_columns", [$this, 'modifyColumns'], 10, 1); + + // Populate custom columns + add_filter("manage_{$this->posttype->name}_posts_custom_column", [$this, 'populateColumns'], 10, 2); + + // Run filter to make columns sortable. + add_filter('manage_edit-'.$this->posttype->name.'_sortable_columns', [$this, 'setSortableColumns']); + + // Run action that sorts columns on request. + add_action('pre_get_posts', [$this, 'sortSortableColumns']); + } + } + + /** + * Register the new Post Type. + * + * @return void + */ + public function registerPostType() + { + // Create options for the PostType. + $options = $this->createOptions(); + + // Check that the post type doesn't already exist. + if (!post_type_exists($this->posttype->name)) { + // Register the post type. + register_post_type($this->posttype->name, $options); + } + } + + /** + * Modify the existing Post Type. + * + * @return array + */ + public function modifyPostType(array $args, string $posttype) + { + if ($posttype !== $this->posttype->name) { + return $args; + } + + // Create options for the PostType. + $options = $this->createOptions(); + + $args = array_replace_recursive($args, $options); + + return $args; + } + + /** + * Create the required names for the PostType. + * + * @return void + */ + public function createNames() + { + // Names required for the PostType. + $required = [ + 'name', + 'singular', + 'plural', + 'slug', + ]; + + foreach ($required as $key) { + // If the name is set, assign it. + if (isset($this->posttype->names[$key])) { + $this->posttype->$key = $this->posttype->names[$key]; + continue; + } + + // If the key is not set and is singular or plural. + if (in_array($key, ['singular', 'plural'])) { + // create a human friendly name + $name = ucwords(strtolower(str_replace(['-', '_'], ' ', $this->posttype->names['name']))); + } + + if ($key === 'slug') { + // Create a slug friendly name. + $name = strtolower(str_replace([' ', '_'], '-', $this->posttype->names['name'])); + } + + // If is plural or slug, append an 's'. + if (in_array($key, ['plural', 'slug'])) { + if (substr($name, strlen($name) - 1, 1) == "y") { + $name = substr($name, 0, strlen($name) - 1) . "ies"; + } else { + $name .= 's'; + } + } + + // Asign the name to the PostType property. + $this->posttype->$key = $name; + } + } + + /** + * Create options for PostType. + * + * @return array + */ + public function createOptions() + { + // Default options. + $options = [ + 'public' => true, + 'rewrite' => [ + 'slug' => $this->posttype->slug + ] + ]; + + // Replace defaults with the options passed. + $options = array_replace_recursive($options, $this->posttype->options); + + // Create and set labels. + if (!isset($options['labels'])) { + $options['labels'] = $this->createLabels(); + } + + // Set the menu icon. + if (!isset($options['menu_icon']) && isset($this->posttype->icon)) { + $options['menu_icon'] = $this->posttype->icon; + } + + return $options; + } + + /** + * Create the labels for the PostType. + * + * @return array + */ + public function createLabels() + { + // Default labels. + $labels = [ + 'name' => $this->posttype->plural, + 'singular_name' => $this->posttype->singular, + 'menu_name' => $this->posttype->plural, + 'all_items' => $this->posttype->plural, + 'add_new' => "Add New", + 'add_new_item' => "Add New {$this->posttype->singular}", + 'edit_item' => "Edit {$this->posttype->singular}", + 'new_item' => "New {$this->posttype->singular}", + 'view_item' => "View {$this->posttype->singular}", + 'search_items' => "Search {$this->posttype->plural}", + 'not_found' => "No {$this->posttype->plural} found", + 'not_found_in_trash' => "No {$this->posttype->plural} found in Trash", + 'parent_item_colon' => "Parent {$this->posttype->singular}:", + ]; + + return array_replace_recursive($labels, $this->posttype->labels); + } + + /** + * Register Taxonomies to the PostType. + * + * @return void + */ + public function registerTaxonomies() + { + if (!empty($this->posttype->taxonomies)) { + foreach ($this->posttype->taxonomies as $taxonomy) { + register_taxonomy_for_object_type($taxonomy, $this->posttype->name); + } + } + } + + /** + * Modify and display filters on the admin edit screen. + * + * @param string $posttype The current screen post type. + * + * @return void + */ + public function modifyFilters($posttype) + { + // First check we are working with the this PostType. + if ($posttype === $this->posttype->name) { + // Calculate what filters to add. + $filters = $this->getFilters(); + + foreach ($filters as $taxonomy) { + // If the taxonomy doesn't exist, ignore it. + if (!taxonomy_exists($taxonomy)) { + continue; + } + + // If the taxonomy is not registered to the post type, continue. + if (!is_object_in_taxonomy($this->posttype->name, $taxonomy)) { + continue; + } + + // Get the taxonomy object. + $tax = get_taxonomy($taxonomy); + + // Start the html for the filter dropdown. + $selected = null; + + if (isset($_GET[$taxonomy])) { + $selected = sanitize_title($_GET[$taxonomy]); + } + + $dropdown_args = [ + 'name' => $taxonomy, + 'value_field' => 'slug', + 'taxonomy' => $tax->name, + 'show_option_all' => $tax->labels->all_items, + 'hierarchical' => $tax->hierarchical, + 'selected' => $selected, + 'orderby' => 'name', + 'hide_empty' => 0, + 'show_count' => 0, + ]; + + // Output screen reader label. + echo ''; + + // Output dropdown for taxonomy. + wp_dropdown_categories($dropdown_args); + } + } + } + + /** + * Calculate the filters for the PostType. + * + * @return array + */ + public function getFilters() + { + // Default filters are empty. + $filters = []; + + // If custom filters have been set, use them. + if (!is_null($this->posttype->filters)) { + return $this->posttype->filters; + } + + // If no custom filters have been set, and there are Taxonomies assigned to the PostType. + if (is_null($this->posttype->filters) && !empty($this->posttype->taxonomies)) { + // Create filters for each taxonomy assigned to the PostType. + return $this->posttype->taxonomies; + } + + return $filters; + } + + /** + * Modify the columns for the PostType. + * + * @param array $columns Default WordPress columns. + * + * @return array + */ + public function modifyColumns($columns) + { + $columns = $this->posttype->columns->modifyColumns($columns); + + return $columns; + } + + /** + * Populate custom columns for the PostType. + * + * @param string $column The column slug. + * @param int $post_id The post ID. + * + * @return void + */ + public function populateColumns($column, $post_id) + { + if (isset($this->posttype->columns->populate[$column])) { + call_user_func_array($this->posttype->columns()->populate[$column], [$column, $post_id]); + } + } + + /** + * Make custom columns sortable. + * + * @param array $columns Default WordPress sortable columns. + * + * @return array + */ + public function setSortableColumns($columns) + { + if (!empty($this->posttype->columns()->sortable)) { + $columns = array_merge($columns, $this->posttype->columns()->sortable); + } + + return $columns; + } + + /** + * Set query to sort custom columns. + * + * @param WP_Query $query The admin screen WP_Query. + * + * @return void + */ + public function sortSortableColumns($query) + { + // don't modify the query if we're not in the post type admin + if (!is_admin() || $query->get('post_type') !== $this->posttype->name) { + return; + } + + $orderby = $query->get('orderby'); + + // if the sorting a custom column + if ($this->posttype->columns()->isSortable($orderby)) { + // get the custom column options + $meta = $this->posttype->columns()->sortableMeta($orderby); + + // determine type of ordering + if (is_string($meta) or !$meta[1]) { + $meta_key = $meta; + $meta_value = 'meta_value'; + } else { + $meta_key = $meta[0]; + $meta_value = 'meta_value_num'; + } + + // set the custom order + $query->set('meta_key', $meta_key); + $query->set('orderby', $meta_value); + } + } +} diff --git a/src/Registrars/TaxonomyRegistrar.php b/src/Registrars/TaxonomyRegistrar.php new file mode 100644 index 0000000..37bfa5c --- /dev/null +++ b/src/Registrars/TaxonomyRegistrar.php @@ -0,0 +1,268 @@ +taxonomy = $taxonomy; + } + + /** + * Register the Taxonomy to WordPress. + * + * @return void + */ + public function register() + { + // Create the required names to register the Taxonomy. + $this->createNames(); + + // Register the taxonomy, set priority to 9 to register before PostTypes. + add_action('init', [$this, 'registerTaxonomy'], 9); + + // Assign taxonomy to post type objects. + add_action('init', [$this, 'registerTaxonomyToObjects']); + + if (isset($this->taxonomy->columns)) { + // Modify the columns for the Taxonomy. + add_filter("manage_edit-{$this->taxonomy->name}_columns", [$this, 'modifyColumns']); + + // Populate the columns for the Taxonomy. + add_filter("manage_{$this->taxonomy->name}_custom_column", [$this, 'populateColumns'], 10, 3); + + // Set custom sortable columns. + add_filter("manage_edit-{$this->taxonomy->name}_sortable_columns", [$this, 'setSortableColumns']); + + // Run action that sorts columns on request. + add_action('parse_term_query', [$this, 'sortSortableColumns']); + } + } + + /** + * Register the Taxonomy to WordPress. + * + * @return void + */ + public function registerTaxonomy() + { + // Get the existing taxonomy options if it exists. + $options = (taxonomy_exists($this->taxonomy->name)) ? (array) get_taxonomy($this->taxonomy->name) : []; + + // Create options for the Taxonomy. + $options = array_replace_recursive($options, $this->createOptions()); + + // Register the Taxonomy with WordPress. + register_taxonomy($this->taxonomy->name, null, $options); + } + + /** + * Register the Taxonomy to PostTypes. + * + * @return void + */ + public function registerTaxonomyToObjects() + { + // Register Taxonomy to each of the PostTypes assigned. + if (!empty($this->taxonomy->posttypes)) { + foreach ($this->taxonomy->posttypes as $posttype) { + register_taxonomy_for_object_type($this->taxonomy->name, $posttype); + } + } + } + + /** + * Create names for the Taxonomy. + * + * @return void + */ + public function createNames() + { + $required = [ + 'name', + 'singular', + 'plural', + 'slug', + ]; + + foreach ($required as $key) { + // if the name is set, assign it + if (isset($this->taxonomy->names[$key])) { + $this->taxonomy->$key = $this->taxonomy->names[$key]; + continue; + } + + // if the key is not set and is singular or plural + if (in_array($key, ['singular', 'plural'])) { + // create a human friendly name + $name = ucwords(strtolower(str_replace(['-', '_'], ' ', $this->taxonomy->names['name']))); + } + + if ($key === 'slug') { + // create a slug friendly name + $name = strtolower(str_replace([' ', '_'], '-', $this->taxonomy->names['name'])); + } + + // if is plural or slug, append an 's' + if (in_array($key, ['plural', 'slug'])) { + $name .= 's'; + } + + // asign the name to the PostType property + $this->taxonomy->$key = $name; + } + } + + /** + * Create options for Taxonomy. + * + * @return array + */ + public function createOptions() + { + // Default options. + $options = [ + 'hierarchical' => true, + 'show_admin_column' => true, + 'rewrite' => [ + 'slug' => $this->taxonomy->slug, + ], + ]; + + // Replace defaults with the options passed. + $options = array_replace_recursive($options, $this->taxonomy->options); + + // Create and set labels. + if (!isset($options['labels'])) { + $options['labels'] = $this->createLabels(); + } + + return $options; + } + + /** + * Create labels for the Taxonomy. + * + * @return array + */ + public function createLabels() + { + // Default labels. + $labels = [ + 'name' => $this->taxonomy->plural, + 'singular_name' => $this->taxonomy->singular, + 'menu_name' => $this->taxonomy->plural, + 'all_items' => "All {$this->taxonomy->plural}", + 'edit_item' => "Edit {$this->taxonomy->singular}", + 'view_item' => "View {$this->taxonomy->singular}", + 'update_item' => "Update {$this->taxonomy->singular}", + 'add_new_item' => "Add New {$this->taxonomy->singular}", + 'new_item_name' => "New {$this->taxonomy->singular} Name", + 'parent_item' => "Parent {$this->taxonomy->plural}", + 'parent_item_colon' => "Parent {$this->taxonomy->plural}:", + 'search_items' => "Search {$this->taxonomy->plural}", + 'popular_items' => "Popular {$this->taxonomy->plural}", + 'separate_items_with_commas' => "Seperate {$this->taxonomy->plural} with commas", + 'add_or_remove_items' => "Add or remove {$this->taxonomy->plural}", + 'choose_from_most_used' => "Choose from most used {$this->taxonomy->plural}", + 'not_found' => "No {$this->taxonomy->plural} found", + ]; + + return array_replace($labels, $this->taxonomy->labels); + } + + /** + * Modify the columns for the Taxonomy. + * + * @param array $columns The WordPress default columns. + * + * @return array + */ + public function modifyColumns($columns) + { + $columns = $this->taxonomy->columns->modifyColumns($columns); + + return $columns; + } + + /** + * Populate custom columns for the Taxonomy. + * + * @param string $content The column content. + * @param string $column The current column name. + * @param int $term_id The current term ID. + * + * @return string + */ + public function populateColumns($content, $column, $term_id) + { + if (isset($this->taxonomy->columns->populate[$column])) { + $content = call_user_func_array( + $this->taxonomy->columns()->populate[$column], + [$content, $column, $term_id] + ); + } + + return $content; + } + + /** + * Make custom columns sortable. + * + * @param array $columns Default WordPress sortable columns. + * + * @return array + */ + public function setSortableColumns($columns) + { + if (!empty($this->taxonomy->columns()->sortable)) { + $columns = array_merge($columns, $this->taxonomy->columns()->sortable); + } + + return $columns; + } + + /** + * Set query to sort custom columns. + * + * @param WP_Term_Query $query The admin WP_Query. + * + * @return void + */ + public function sortSortableColumns($query) + { + // Don't modify the query if we're not in the post type admin. + if (!is_admin() || !in_array($this->taxonomy->name, $query->query_vars['taxonomy'] ?? [])) { + return; + } + + // Check the orderby is a custom ordering. + if (isset($_GET['orderby']) && array_key_exists($_GET['orderby'], $this->taxonomy->columns()->sortable)) { + // Get the custom sorting options. + $meta = $this->taxonomy->columns()->sortable[$_GET['orderby']]; + + // Check ordering is not numeric. + if (is_string($meta)) { + $meta_key = $meta; + $orderby = 'meta_value'; + } else { + $meta_key = $meta[0]; + $orderby = 'meta_value_num'; + } + + // Set the sort order. + $query->query_vars['orderby'] = $orderby; + $query->query_vars['meta_key'] = $meta_key; + } + } +} diff --git a/src/Taxonomy.php b/src/Taxonomy.php index 1e27f74..0baf7ce 100644 --- a/src/Taxonomy.php +++ b/src/Taxonomy.php @@ -3,6 +3,8 @@ namespace PostTypes; use PostTypes\Columns; +use PostTypes\Registrars\TaxonomyRegistrar; +use PostTypes\Contracts\Taxonomy as TaxonomyContract; /** * Taxonomy @@ -15,65 +17,79 @@ * @version 2.2.1 * @license https://opensource.org/licenses/mit-license.html MIT License */ -class Taxonomy +class Taxonomy implements TaxonomyContract { /** - * The names passed to the Taxonomy + * The names passed to the Taxonomy. + * * @var mixed */ public $names; /** - * The Taxonomy name + * The Taxonomy name. + * * @var string */ public $name; /** - * The singular label for the Taxonomy + * The singular label for the Taxonomy. + * * @var string */ public $singular; /** - * The plural label for the Taxonomy + * The plural label for the Taxonomy. + * * @var string */ public $plural; /** - * The Taxonomy slug + * The Taxonomy slug. + * * @var string */ public $slug; /** - * Custom options for the Taxonomy + * Custom options for the Taxonomy. + * * @var array */ public $options; /** - * Custom labels for the Taxonomy + * Custom labels for the Taxonomy. + * * @var array */ public $labels; /** - * PostTypes to register the Taxonomy to + * Post types to register the Taxonomy to. + * * @var array */ public $posttypes = []; /** - * The column manager for the Taxonomy + * The column manager for the Taxonomy. + * * @var mixed */ public $columns; /** - * Create a Taxonomy - * @param mixed $names The name(s) for the Taxonomy + * Create a Taxonomy. + * + * @param mixed $names The name(s) for the Taxonomy. + * @param array $options The Taxonomy options. + * @param array $labels The Taxonomy labels + * + * @return void */ public function __construct($names, $options = [], $labels = []) { @@ -85,28 +101,25 @@ public function __construct($names, $options = [], $labels = []) } /** - * Set the names for the Taxonomy - * @param mixed $names The name(s) for the Taxonomy - * @return $this + * Set the names for the Taxonomy. + * + * @param mixed $names The name(s) for the Taxonomy. + * + * @return PostType\Taxonomy */ public function names($names) { - if (is_string($names)) { - $names = ['name' => $names]; - } - - $this->names = $names; - - // create names for the Taxonomy - $this->createNames(); + $this->names = is_string($names) ? ['name' => $names] : $names; return $this; } /** - * Set options for the Taxonomy - * @param array $options - * @return $this + * Set options for the Taxonomy. + * + * @param array $options An array of Taxonomy options. + * + * @return PostType\Taxonomy */ public function options(array $options = []) { @@ -116,9 +129,11 @@ public function options(array $options = []) } /** - * Set the Taxonomy labels - * @param array $labels - * @return $this + * Set the Taxonomy labels. + * + * @param array $labels The Taxonomy labels. + * + * @return PostType\Taxonomy */ public function labels(array $labels = []) { @@ -128,23 +143,22 @@ public function labels(array $labels = []) } /** - * Assign a PostType to register the Taxonomy to - * @param mixed $posttypes - * @return $this + * Register post types to the taxonomy. + * + * @param mixed $posttypes An array of post types names. + * + * @return PostType\Taxonomy */ public function posttype($posttypes) { - $posttypes = is_string($posttypes) ? [$posttypes] : $posttypes; - - foreach ($posttypes as $posttype) { - $this->posttypes[] = $posttype; - } + $this->posttypes = is_string($posttypes) ? [$posttypes] : $posttypes; return $this; } /** - * Get the Column Manager for the Taxonomy + * Get the Column Manager for the Taxonomy. + * * @return Columns */ public function columns() @@ -157,228 +171,12 @@ public function columns() } /** - * Register the Taxonomy to WordPress + * Register the Taxonomy to WordPress. + * * @return void */ public function register() { - // register the taxonomy, set priority to 9 - // so taxonomies are registered before PostTypes - add_action('init', [$this, 'registerTaxonomy'], 9); - - // assign taxonomy to post type objects - add_action('init', [$this, 'registerTaxonomyToObjects']); - - if (isset($this->columns)) { - // modify the columns for the Taxonomy - add_filter("manage_edit-{$this->name}_columns", [$this, 'modifyColumns']); - - // populate the columns for the Taxonomy - add_filter("manage_{$this->name}_custom_column", [$this, 'populateColumns'], 10, 3); - - // set custom sortable columns - add_filter("manage_edit-{$this->name}_sortable_columns", [$this, 'setSortableColumns']); - - // run action that sorts columns on request - add_action('parse_term_query', [$this, 'sortSortableColumns']); - } - } - - /** - * Register the Taxonomy to WordPress - * @return void - */ - public function registerTaxonomy() - { - // Get the existing taxonomy options if it exists. - $options = (taxonomy_exists($this->name)) ? (array) get_taxonomy($this->name) : []; - - // create options for the Taxonomy. - $options = array_replace_recursive($options, $this->createOptions()); - - // register the Taxonomy with WordPress. - register_taxonomy($this->name, null, $options); - } - - /** - * Register the Taxonomy to PostTypes - * @return void - */ - public function registerTaxonomyToObjects() - { - // register Taxonomy to each of the PostTypes assigned - if (!empty($this->posttypes)) { - foreach ($this->posttypes as $posttype) { - register_taxonomy_for_object_type($this->name, $posttype); - } - } - } - - /** - * Create names for the Taxonomy - * @return void - */ - public function createNames() - { - $required = [ - 'name', - 'singular', - 'plural', - 'slug', - ]; - - foreach ($required as $key) { - // if the name is set, assign it - if (isset($this->names[$key])) { - $this->$key = $this->names[$key]; - continue; - } - - // if the key is not set and is singular or plural - if (in_array($key, ['singular', 'plural'])) { - // create a human friendly name - $name = ucwords(strtolower(str_replace(['-', '_'], ' ', $this->names['name']))); - } - - if ($key === 'slug') { - // create a slug friendly name - $name = strtolower(str_replace([' ', '_'], '-', $this->names['name'])); - } - - // if is plural or slug, append an 's' - if (in_array($key, ['plural', 'slug'])) { - $name .= 's'; - } - - // asign the name to the PostType property - $this->$key = $name; - } - } - - /** - * Create options for Taxonomy - * @return array Options to pass to register_taxonomy - */ - public function createOptions() - { - // default options - $options = [ - 'hierarchical' => true, - 'show_admin_column' => true, - 'rewrite' => [ - 'slug' => $this->slug, - ], - ]; - - // replace defaults with the options passed - $options = array_replace_recursive($options, $this->options); - - // create and set labels - if (!isset($options['labels'])) { - $options['labels'] = $this->createLabels(); - } - - return $options; - } - - /** - * Create labels for the Taxonomy - * @return array - */ - public function createLabels() - { - // default labels - $labels = [ - 'name' => $this->plural, - 'singular_name' => $this->singular, - 'menu_name' => $this->plural, - 'all_items' => "All {$this->plural}", - 'edit_item' => "Edit {$this->singular}", - 'view_item' => "View {$this->singular}", - 'update_item' => "Update {$this->singular}", - 'add_new_item' => "Add New {$this->singular}", - 'new_item_name' => "New {$this->singular} Name", - 'parent_item' => "Parent {$this->plural}", - 'parent_item_colon' => "Parent {$this->plural}:", - 'search_items' => "Search {$this->plural}", - 'popular_items' => "Popular {$this->plural}", - 'separate_items_with_commas' => "Seperate {$this->plural} with commas", - 'add_or_remove_items' => "Add or remove {$this->plural}", - 'choose_from_most_used' => "Choose from most used {$this->plural}", - 'not_found' => "No {$this->plural} found", - ]; - - return array_replace($labels, $this->labels); - } - - /** - * Modify the columns for the Taxonomy - * @param array $columns The WordPress default columns - * @return array - */ - public function modifyColumns($columns) - { - $columns = $this->columns->modifyColumns($columns); - - return $columns; - } - - /** - * Populate custom columns for the Taxonomy - * @param string $content - * @param string $column - * @param int $term_id - */ - public function populateColumns($content, $column, $term_id) - { - if (isset($this->columns->populate[$column])) { - $content = call_user_func_array($this->columns()->populate[$column], [$content, $column, $term_id]); - } - - return $content; - } - - /** - * Make custom columns sortable - * @param array $columns Default WordPress sortable columns - */ - public function setSortableColumns($columns) - { - if (!empty($this->columns()->sortable)) { - $columns = array_merge($columns, $this->columns()->sortable); - } - - return $columns; - } - - /** - * Set query to sort custom columns - * @param WP_Term_Query $query - */ - public function sortSortableColumns($query) - { - // don't modify the query if we're not in the post type admin - if (!is_admin() || !in_array($this->name, $query->query_vars['taxonomy'] ?? [])) { - return; - } - - // check the orderby is a custom ordering - if (isset($_GET['orderby']) && array_key_exists($_GET['orderby'], $this->columns()->sortable)) { - // get the custom sorting options - $meta = $this->columns()->sortable[$_GET['orderby']]; - - // check ordering is not numeric - if (is_string($meta)) { - $meta_key = $meta; - $orderby = 'meta_value'; - } else { - $meta_key = $meta[0]; - $orderby = 'meta_value_num'; - } - - // set the sort order - $query->query_vars['orderby'] = $orderby; - $query->query_vars['meta_key'] = $meta_key; - } + (new TaxonomyRegistrar($this))->register(); } } diff --git a/tests/PostTypeTest.php b/tests/PostTypeTest.php index 1769bcc..318c61a 100644 --- a/tests/PostTypeTest.php +++ b/tests/PostTypeTest.php @@ -147,119 +147,4 @@ public function columnsReturnsInstanceOfColumns() { $this->assertInstanceOf(Columns::class, $this->books->columns()); } - - /** @test */ - public function namesCreatedFromName() - { - $this->books->createNames(); - - $this->assertEquals($this->books->name, 'book'); - $this->assertEquals($this->books->singular, 'Book'); - $this->assertEquals($this->books->plural, 'Books'); - $this->assertEquals($this->books->slug, 'books'); - } - - /** @test */ - public function passedNamesAreUsed() - { - $names = [ - 'name' => 'book', - 'singular' => 'Single Book', - 'plural' => 'Multiple Books', - 'slug' => 'slug_books', - ]; - - $this->books->names($names); - - $this->books->createNames(); - - $this->assertEquals($this->books->name, 'book'); - $this->assertEquals($this->books->singular, 'Single Book'); - $this->assertEquals($this->books->plural, 'Multiple Books'); - $this->assertEquals($this->books->slug, 'slug_books'); - } - - /** @test */ - public function defaultOptionsUsedIfNotSet() - { - // generated options - $options = $this->books->createOptions(); - - // expected options - $defaults = [ - 'public' => true, - 'labels' => $this->books->createLabels(), - 'rewrite' => [ - 'slug' => $this->books->slug - ] - ]; - - $this->assertEquals($options, $defaults); - } - - /** @test */ - public function defaultLabelsAreGenerated() - { - $labels = $this->books->createLabels(); - - $defaults = [ - 'name' => $this->books->plural, - 'singular_name' => $this->books->singular, - 'menu_name' => $this->books->plural, - 'all_items' => $this->books->plural, - 'add_new' => "Add New", - 'add_new_item' => "Add New {$this->books->singular}", - 'edit_item' => "Edit {$this->books->singular}", - 'new_item' => "New {$this->books->singular}", - 'view_item' => "View {$this->books->singular}", - 'search_items' => "Search {$this->books->plural}", - 'not_found' => "No {$this->books->plural} found", - 'not_found_in_trash' => "No {$this->books->plural} found in Trash", - 'parent_item_colon' => "Parent {$this->books->singular}:", - ]; - - $this->assertEquals($labels, $defaults); - } - - /** @test */ - public function filtersAreEmptyIfNotSetAndNoTaxonomies() - { - $filters = $this->books->getFilters(); - - $this->assertEquals($filters, []); - } - - /** @test */ - public function filtersAreSameAsTaxonomyIfNotSet() - { - $this->books->taxonomy('genre'); - - $filters = $this->books->getFilters(); - - $this->assertEquals($filters, ['genre']); - } - - /** @test */ - public function filtersAreWhatAssignedIfPassed() - { - $this->books->filters(['genre', 'published']); - - $this->books->taxonomy('genre'); - - $filters = $this->books->getFilters(); - - $this->assertEquals($filters, ['genre', 'published']); - } - - /** @test */ - public function filtersAreEmptyIfSetWithEmptyArray() - { - $this->books->filters([]); - - $this->books->taxonomy('genre'); - - $filters = $this->books->getFilters(); - - $this->assertEquals($filters, []); - } } diff --git a/tests/TaxonomyTest.php b/tests/TaxonomyTest.php index 2883c19..a0bb480 100644 --- a/tests/TaxonomyTest.php +++ b/tests/TaxonomyTest.php @@ -103,56 +103,6 @@ public function canAddMultiplePostTypes() $this->assertEquals($genres->posttypes, ['books', 'films']); } - /** @test */ - public function namesCreatedFromName() - { - $this->genres->createNames(); - - $this->assertEquals($this->genres->name, 'genre'); - $this->assertEquals($this->genres->singular, 'Genre'); - $this->assertEquals($this->genres->plural, 'Genres'); - $this->assertEquals($this->genres->slug, 'genres'); - } - - /** @test */ - public function passedNamesAreUsed() - { - $names = [ - 'name' => 'genre', - 'singular' => 'Single Genre', - 'plural' => 'Multiple Genres', - 'slug' => 'slug-genres', - ]; - - $this->genres->names($names); - - $this->genres->createNames(); - - $this->assertEquals($this->genres->name, 'genre'); - $this->assertEquals($this->genres->singular, 'Single Genre'); - $this->assertEquals($this->genres->plural, 'Multiple Genres'); - $this->assertEquals($this->genres->slug, 'slug-genres'); - } - - /** @test */ - public function defaultOptionsUsedIfNotSet() - { - // generated options - $options = $this->genres->createOptions(); - - // expected options - $defaults = [ - 'hierarchical' => true, - 'show_admin_column' => true, - 'labels' => $this->genres->createLabels(), - 'rewrite' => [ - 'slug' => $this->genres->slug, - ], - ]; - - $this->assertEquals($options, $defaults); - } - /** @test */ public function columnsIsNullOnInstantiation() {