diff options
| author | Daniel Weipert <git@mail.dweipert.de> | 2024-06-07 21:48:12 +0200 | 
|---|---|---|
| committer | Daniel Weipert <git@mail.dweipert.de> | 2024-06-07 21:48:12 +0200 | 
| commit | 3a837ef6bb8445ba8e944741d6f00f763c12e714 (patch) | |
| tree | f00919fd1a5546e212399d2083d7a65750c93eb0 | |
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Readme.md | 77 | ||||
| -rw-r--r-- | composer.json | 15 | ||||
| -rw-r--r-- | src/Columns.php | 234 | ||||
| -rw-r--r-- | src/PostType.php | 535 | ||||
| -rw-r--r-- | src/Taxonomy.php | 376 | 
6 files changed, 1238 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..372edc1 --- /dev/null +++ b/Readme.md @@ -0,0 +1,77 @@ +# PostTypes + +> Simple WordPress custom post types. + +## Requirements + +* PHP >=7.2 +* [Composer](https://getcomposer.org/) +* [WordPress](https://wordpress.org) >=5.1 + +## Installation + +#### Install with composer + +Run the following in your terminal to install PostTypes with [Composer](https://getcomposer.org/). + +``` +$ composer require dweipert/posttypes +``` + +PostTypes uses [PSR-4](https://www.php-fig.org/psr/psr-4/) autoloading and can be used with the Composer's autoloader. Below is a basic example of getting started, though your setup may be different depending on how you are using Composer. + +```php +require __DIR__ . '/vendor/autoload.php'; + +use PostTypes\PostType; + +$books = new PostType( 'book' ); + +$books->register(); +``` + +See Composer's [basic usage](https://getcomposer.org/doc/01-basic-usage.md#autoloading) guide for details on working with Composer and autoloading. + +## Basic Usage + +Below is a basic example of setting up a simple book post type with a genre taxonomy. + +```php +// Require the Composer autoloader. +require __DIR__ . '/vendor/autoload.php'; + +// Import PostTypes. +use PostTypes\PostType; +use PostTypes\Taxonomy; + +// Create a book post type. +$books = new PostType( 'book' ); + +// Attach the genre taxonomy (which is created below). +$books->taxonomy( 'genre' ); + +// Hide the date and author columns. +$books->columns()->hide( [ 'date', 'author' ] ); + +// Set the Books menu icon. +$books->icon( 'dashicons-book-alt' ); + +// Register the post type to WordPress. +$books->register(); + +// Create a genre taxonomy. +$genres = new Taxonomy( 'genre' ); + +// Set options for the taxonomy. +$genres->options( [ +    'hierarchical' => false, +] ); + +// Register the taxonomy to WordPress. +$genres->register(); +``` + +## Notes + +* The class has no methods for making custom fields for post types, use [Advanced Custom Fields](https://advancedcustomfields.com) +* Maintained under the [Semantic Versioning Guide](https://semver.org) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a20a7ef --- /dev/null +++ b/composer.json @@ -0,0 +1,15 @@ +{ +    "name": "dweipert/posttypes", +    "description": "Simple WordPress custom post types.", +    "keywords": ["wordpress", "post-types"], +    "require": { +        "php": ">=7.2" +    }, +    "autoload": { +        "psr-4": { +            "PostTypes\\": "src/" +        } +    }, +    "minimum-stability": "dev", +    "prefer-stable": true +} diff --git a/src/Columns.php b/src/Columns.php new file mode 100644 index 0000000..51f0cd7 --- /dev/null +++ b/src/Columns.php @@ -0,0 +1,234 @@ +<?php + +namespace PostTypes; + +/** + * Columns + * + * Used to help manage a post types columns in the admin table + */ +class Columns +{ +    /** +     * Holds an array of all the defined columns. +     * +     * @var array +     */ +    public $items = []; + +    /** +     * An array of columns to add. +     * +     * @var array +     */ +    public $add = []; + +    /** +     * An array of columns to hide. +     * +     * @var array +     */ +    public $hide = []; + +    /** +     * An array of columns to reposition. +     * +     * @var array +     */ +    public $positions = []; + +    /** +     * An array of custom populate callbacks. +     * +     * @var array +     */ +    public $populate = []; + +    /** +     * An array of columns that are sortable. +     * +     * @var array +     */ +    public $sortable = []; + +    /** +     * Set the all columns +     * @param array $columns an array of all the columns to replace +     */ +    public function set($columns) +    { +        $this->items = $columns; +    } + +    /** +     * Add a new column +     * @param string  $column   the slug of the column +     * @param string  $label    the label for the column +     */ +    public function add($columns, $label = null) +    { + +        if (!is_array($columns)) { +            $columns = [$columns => $label]; +        } + +        foreach ($columns as $column => $label) { +            if (is_null($label)) { +                $label = str_replace(['_', '-'], ' ', ucfirst($column)); +            } + +            $this->add[$column] = $label; +        } + +        return $this; +    } + +    /** +     * Add a column to hide +     * @param  string $column the slug of the column to hdie +     */ +    public function hide($columns) +    { +        if (!is_array($columns)) { +            $columns = [$columns]; +        } + +        foreach ($columns as $column) { +            $this->hide[] = $column; +        } + +        return $this; +    } + +    /** +     * Set a custom callback to populate a column +     * @param  string $column   the column slug +     * @param  mixed  $callback callback function +     */ +    public function populate($column, $callback) +    { +        $this->populate[$column] = $callback; + +        return $this; +    } + +    /** +     * Define the postion for a columns +     * @param  string  $columns  an array of columns +     */ +    public function order($columns) +    { +        foreach ($columns as $column => $position) { +            $this->positions[$column] = $position; +        } + +        return $this; +    } + +    /** +     * 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 +     */ +    public function sortable($sortable) +    { +        foreach ($sortable as $column => $options) { +            $this->sortable[$column] = $options; +        } + +        return $this; +    } + +    /** +     * Check if an orderby field is a custom sort option. +     * @param  string  $orderby  the orderby value from query params +     */ +    public function isSortable($orderby) +    { +        if (is_string($orderby) && array_key_exists($orderby, $this->sortable)) { +            return true; +        } + +        foreach ($this->sortable as $column => $options) { +            if (is_string($options) && $options === $orderby) { +                return true; +            } +            if (is_array($options) && isset($options[0]) && $options[0] === $orderby) { +                return true; +            } +        } + +        return false; +    } + +    /** +     * Get meta key for an orderby. +     * @param  string  $orderby  the orderby value from query params +     */ +    public function sortableMeta($orderby) +    { +        if (array_key_exists($orderby, $this->sortable)) { +            return $this->sortable[$orderby]; +        } + +        foreach ($this->sortable as $column => $options) { +            if (is_string($options) && $options === $orderby) { +                return $options; +            } +            if (is_array($options) && isset($options[0]) && $options[0] === $orderby) { +                return $options; +            } +        } + +        return ''; +    } + +    /** +     * Modify the columns for the object +     * @param  array  $columns WordPress default columns +     * @return array           The modified columns +     */ +    public function modifyColumns($columns) +    { +        // if user defined set columns, return those +        if (!empty($this->items)) { +            return $this->items; +        } + +        // add additional columns +        if (!empty($this->add)) { +            foreach ($this->add as $key => $label) { +                $columns[$key] = $label; +            } +        } + +        // unset hidden columns +        if (!empty($this->hide)) { +            foreach ($this->hide as $key) { +                unset($columns[$key]); +            } +        } + +        // 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 +                $index = array_search($key, array_keys($columns)); +                // retrieve the element in the array of columns +                $item = array_slice($columns, $index, 1); +                // remove item from the array +                unset($columns[$key]); + +                // 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 +                $columns = $start + $item + $end; +            } +        } + +        return $columns; +    } +} diff --git a/src/PostType.php b/src/PostType.php new file mode 100644 index 0000000..97edc02 --- /dev/null +++ b/src/PostType.php @@ -0,0 +1,535 @@ +<?php + +namespace PostTypes; + +use PostTypes\Columns; + +/** + * PostType + */ +class PostType +{ +    /** +     * The names passed to the PostType +     * @var array +     */ +    public $names; + +    /** +     * The name for the PostType +     * @var string +     */ +    public $name; + +    /** +     * The singular for the PostType +     * @var string +     */ +    public $singular; + +    /** +     * The plural name for the PostType +     * @var string +     */ +    public $plural; + +    /** +     * The slug for the PostType +     * @var string +     */ +    public $slug; + +    /** +     * Options for the PostType +     * @var array +     */ +    public $options; + +    /** +     * Labels for the PostType +     * @var array +     */ +    public $labels; + +    /** +     * Taxonomies for the PostType +     * @var array +     */ +    public $taxonomies = []; + +    /** +     * Filters for the PostType +     * @var mixed +     */ +    public $filters; + +    /** +     * The menu icon for the PostType +     * @var string +     */ +    public $icon; + +    /** +     * The column manager for the PostType +     * @var mixed +     */ +    public $columns; + +    /** +     * Create a PostType +     * @param mixed $names   A string for the name, or an array of names +     * @param array $options An array of options for the PostType +     */ +    public function __construct($names, $options = [], $labels = []) +    { +        // assign names to the PostType +        $this->names($names); + +        // assign custom options to the PostType +        $this->options($options); + +        // 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 +     */ +    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(); + +        return $this; +    } + +    /** +     * Set the options for the PostType +     * @param  array $options An array of options for the PostType +     * @return $this +     */ +    public function options(array $options) +    { +        $this->options = $options; + +        return $this; +    } + +    /** +     * Set the labels for the PostType +     * @param  array $labels An array of labels for the PostType +     * @return $this +     */ +    public function labels(array $labels) +    { +        $this->labels = $labels; + +        return $this; +    } + +    /** +     * Add a Taxonomy to the PostType +     * @param  mixed $taxonomies The Taxonomy name(s) to add +     * @return $this +     */ +    public function taxonomy($taxonomies) +    { +        $taxonomies = is_string($taxonomies) ? [$taxonomies] : $taxonomies; + +        foreach ($taxonomies as $taxonomy) { +            $this->taxonomies[] = $taxonomy; +        } + +        return $this; +    } + +    /** +     * Add filters to the PostType +     * @param  array $filters An array of Taxonomy filters +     * @return $this +     */ +    public function filters(array $filters) +    { +        $this->filters = $filters; + +        return $this; +    } + +    /** +     * Set the menu icon for the PostType +     * @param  string $icon A dashicon class for the menu icon +     * @return $this +     */ +    public function icon($icon) +    { +        $this->icon = $icon; + +        return $this; +    } + +    /** +     * Flush rewrite rules +     * @link https://codex.wordpress.org/Function_Reference/flush_rewrite_rules +     * @param  boolean $hard +     * @return void +     */ +    public function flush($hard = true) +    { +        flush_rewrite_rules($hard); +    } + +    /** +     * Get the Column Manager for the PostType +     * @return PostTypes\Columns +     */ +    public function columns() +    { +        if (!isset($this->columns)) { +            $this->columns = new Columns; +        } + +        return $this->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. +     * +     * @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' => "Erstellen", +            'add_new_item' => "{$this->singular} erstellen", +            'edit_item' => "{$this->singular} bearbeiten", +            'new_item' => "{$this->singular} erstellen", +            'view_item' => "{$this->singular} anschauen", +            'search_items' => "{$this->plural} suchen", +            'not_found' => "Kein {$this->plural} gefunden", +            'not_found_in_trash' => "Es wurden keine {$this->plural} im Papierkorb gefunden.", +            'parent_item_colon' => "Eltern-{$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 '<label class="screen-reader-text" for="cat">' . $tax->labels->filter_by_item . '</label>'; + +                // 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) +    { +        // 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); +        } +    } +} diff --git a/src/Taxonomy.php b/src/Taxonomy.php new file mode 100644 index 0000000..a7aca5b --- /dev/null +++ b/src/Taxonomy.php @@ -0,0 +1,376 @@ +<?php + +namespace PostTypes; + +use PostTypes\Columns; + +/** + * Taxonomy + */ +class Taxonomy +{ +    /** +     * The names passed to the Taxonomy +     * @var mixed +     */ +    public $names; + +    /** +     * The Taxonomy name +     * @var string +     */ +    public $name; + +    /** +     * The singular label for the Taxonomy +     * @var string +     */ +    public $singular; + +    /** +     * The plural label for the Taxonomy +     * @var string +     */ +    public $plural; + +    /** +     * The Taxonomy slug +     * @var string +     */ +    public $slug; + +    /** +     * Custom options for the Taxonomy +     * @var array +     */ +    public $options; + +    /** +     * Custom labels for the Taxonomy +     * @var array +     */ +    public $labels; + +    /** +     * PostTypes to register the Taxonomy to +     * @var array +     */ +    public $posttypes = []; + +    /** +     * The column manager for the Taxonomy +     * @var mixed +     */ +    public $columns; + +    /** +     * Create a Taxonomy +     * @param mixed $names The name(s) for the Taxonomy +     */ +    public function __construct($names, $options = [], $labels = []) +    { +        $this->names($names); + +        $this->options($options); + +        $this->labels($labels); +    } + +    /** +     * Set the names for the Taxonomy +     * @param  mixed $names The name(s) for the Taxonomy +     * @return $this +     */ +    public function names($names) +    { +        if (is_string($names)) { +            $names = ['name' => $names]; +        } + +        $this->names = $names; + +        // create names for the Taxonomy +        $this->createNames(); + +        return $this; +    } + +    /** +     * Set options for the Taxonomy +     * @param  array $options +     * @return $this +     */ +    public function options(array $options = []) +    { +        $this->options = $options; + +        return $this; +    } + +    /** +     * Set the Taxonomy labels +     * @param  array  $labels +     * @return $this +     */ +    public function labels(array $labels = []) +    { +        $this->labels = $labels; + +        return $this; +    } + +    /** +     * Assign a PostType to register the Taxonomy to +     * @param  mixed $posttypes +     * @return $this +     */ +    public function posttype($posttypes) +    { +        $posttypes = is_string($posttypes) ? [$posttypes] : $posttypes; + +        foreach ($posttypes as $posttype) { +            $this->posttypes[] = $posttype; +        } + +        return $this; +    } + +    /** +     * Get the Column Manager for the Taxonomy +     * @return Columns +     */ +    public function columns() +    { +        if (!isset($this->columns)) { +            $this->columns = new Columns; +        } + +        return $this->columns; +    } + +    /** +     * 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' => "Alle {$this->plural}", +            'edit_item' => "{$this->singular} bearbeiten", +            'view_item' => "{$this->singular} anschauen", +            'update_item' => "{$this->singular} aktualisieren", +            'add_new_item' => "{$this->singular} hinzufügen", +            'new_item_name' => "Neuer {$this->singular} Name", +            'parent_item' => "Übergeordnete {$this->plural}", +            'parent_item_colon' => "Übergeordnete {$this->plural}:", +            'search_items' => "{$this->plural} suchen", +            'popular_items' => "Häufig genutzte {$this->plural}", +            'separate_items_with_commas' => "Trenne {$this->plural} mit Komma", +            'add_or_remove_items' => "{$this->plural} hinzufügen oder entfernen", +            'choose_from_most_used' => "Wählen aus den meistgenutzten {$this->plural}", +            'not_found' => "Keine {$this->plural} gefunden", +        ]; + +        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; +        } +    } +}  | 
