├── .gitignore ├── README.md ├── composer.json └── src ├── Model └── Post.php └── PostType └── AbstractPostType.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | composer.lock 3 | vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delicious Brains WordPress Post Types 2 | 3 | This is a WordPress library for registering, reading and writing custom post types. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deliciousbrains/wp-post-types", 3 | "description": "WordPress library for registering, reading and writing custom post types.", 4 | "license": "GPL-2.0-or-later", 5 | "authors": [ 6 | { 7 | "name": "Delicious Brains", 8 | "email": "nom@deliciousbrains.com", 9 | "homepage": "https://deliciousbrains.com/" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "DeliciousBrains\\WPPostTypes\\": "src/" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Model/Post.php: -------------------------------------------------------------------------------- 1 | post = self::get_post_object( $data ); 17 | } 18 | 19 | /** 20 | * Converts the data into a wordpress post object 21 | * 22 | * @static 23 | * 24 | * @param mixed $data 25 | * 26 | * @return \WP_Post 27 | */ 28 | public static function get_post_object( $data = null ) { 29 | if ( is_object( $data ) && is_a( $data, get_called_class() ) ) { 30 | return $data->post; 31 | } 32 | 33 | if ( is_array( $data ) ) { 34 | return new \WP_Post( (object) $data ); 35 | } 36 | 37 | if ( is_object( $data ) && $data instanceof \WP_Post ) { 38 | return $data; 39 | } 40 | if ( is_object( $data ) ) { 41 | return new \WP_Post( $data ); 42 | } 43 | 44 | if ( is_numeric( $data ) && is_integer( $data + 0 ) ) { 45 | return get_post( $data ); 46 | 47 | } 48 | 49 | global $post; 50 | 51 | return $post; 52 | } 53 | 54 | public function __call( $name, $args ) { 55 | if ( function_exists( $name ) ) { 56 | global $post; 57 | $post = $this; 58 | setup_postdata( $this ); 59 | 60 | return call_user_func_array( $name, $args ); 61 | } elseif ( function_exists( "wp_" . $name ) ) { 62 | $name = "wp_" . $name; 63 | global $post; 64 | $post = $this; 65 | setup_postdata( $this ); 66 | 67 | return call_user_func_array( $name, $args ); 68 | } else { 69 | trigger_error( 'Attempt to call non existent method ' . $name . ' on class ' . get_class( $this ) ); 70 | } 71 | } 72 | 73 | /** 74 | * Proxy magic properties to WP_Post 75 | * 76 | * @param string $name 77 | * 78 | * @return mixed 79 | */ 80 | public function __get( $name ) { 81 | return $this->post->$name; 82 | } 83 | 84 | /** 85 | * Proxy magic properties to WP_Post 86 | * 87 | * @param string $name 88 | * @param mixed $value 89 | * 90 | * @return mixed 91 | */ 92 | public function __set( $name, $value ) { 93 | return $this->post->$name = $value; 94 | } 95 | 96 | /** 97 | * Proxy magic properties to WP_Post 98 | * 99 | * @param string $name 100 | * 101 | * @return mixed 102 | */ 103 | public function __isset( $name ) { 104 | return isset( $this->post->$name ); 105 | } 106 | 107 | public function title() { 108 | return apply_filters( 'the_title', $this->post_title, $this->ID ); 109 | } 110 | 111 | public function content() { 112 | return apply_filters( 'the_content', $this->post_content ); 113 | } 114 | 115 | public function permalink() { 116 | return get_permalink( $this->ID ); 117 | } 118 | 119 | /** 120 | * Gets the metadata (custom fields) for the post 121 | * 122 | * @param string $name 123 | * @param bool $default 124 | * @param bool $single 125 | * 126 | * @return array|string 127 | */ 128 | public function meta( $name, $default = false, $single = true ) { 129 | $meta = get_post_meta( $this->ID, $name, $single ); 130 | 131 | if ( ! $meta && ! $single ) { 132 | $meta = $default; 133 | } 134 | 135 | return $meta; 136 | } 137 | 138 | /** 139 | * Get the URL of the featured image 140 | * 141 | * @param string $image_size 142 | * 143 | * @return string|false 144 | */ 145 | public function featured_image_url( $image_size = 'thumbnail' ) { 146 | $attachment_id = $this->meta( '_thumbnail_id' ); 147 | if ( ! $attachment_id ) { 148 | return false; 149 | } 150 | 151 | $image = wp_get_attachment_image_src( $attachment_id, $image_size ); 152 | if ( $image && isset( $image[0] ) ) { 153 | return $image[0]; 154 | } 155 | 156 | return false; 157 | } 158 | 159 | protected static function get_post_type() { 160 | $parts = explode( '\\', get_called_class() ); 161 | $search = 'Model'; 162 | $replace = 'PostType'; 163 | $parts = array_map( function ( $part ) use ( $search, $replace ) { 164 | return $part == $search ? $replace : $part; 165 | }, $parts ); 166 | 167 | $type = implode( '\\', $parts ); 168 | 169 | if ( class_exists( $type ) ) { 170 | return call_user_func( array( $type, 'get_post_type' ) ); 171 | } 172 | 173 | return 'post'; 174 | } 175 | 176 | /** 177 | * Create new post in the database. 178 | * 179 | * @param string $title 180 | * @param string $content 181 | * @param array $args 182 | * 183 | * @return int|\WP_Error 184 | */ 185 | public static function create( $title, $content, $args = array() ) { 186 | $defaults = array( 187 | 'post_title' => $title, 188 | 'post_status' => 'publish', 189 | 'post_type' => self::get_post_type(), 190 | 'post_content' => $content, 191 | ); 192 | 193 | return wp_insert_post( array_merge( $args, $defaults ) ); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/PostType/AbstractPostType.php: -------------------------------------------------------------------------------- 1 | type = self::get_post_type(); 18 | $this->single = $this->single ? $this->single : self::get_post_type( '' ); 19 | if ( empty( $this->plural ) ) { 20 | $this->plural = $this->single . 's'; 21 | } 22 | $this->args = $args; 23 | } 24 | 25 | public function init() { 26 | add_action( 'init', array( $this, 'register' ) ); 27 | add_filter( 'manage_edit-' . $this->type . '_columns', array( $this, 'get_columns' ) ); 28 | add_action( 'manage_' . $this->type . '_posts_custom_column', array( $this, 'render_columns' ) ); 29 | add_action( 'add_meta_boxes', function () { 30 | remove_meta_box( 'wpseo_meta', $this->type, 'normal' ); 31 | }, 100 ); 32 | add_action( 'template_redirect', array( $this, 'redirect_post_type_pages' ) ); 33 | add_filter( 'wpseo_sitemap_exclude_post_type', array( $this, 'exclude_post_type_from_site_map' ), 10, 2 ); 34 | if ( ! $this->block_editor ) { 35 | add_filter( 'use_block_editor_for_post_type', function ( $use_block_editor, $post_type ) { 36 | if ( $this->type === $post_type ) { 37 | return false; 38 | } 39 | 40 | return $use_block_editor; 41 | }, 10, 2 ); 42 | } 43 | } 44 | 45 | public static function get_post_type( $prefix = 'dbi_' ) { 46 | $parts = explode( '\\', get_called_class() ); 47 | 48 | return $prefix . strtolower( array_pop( $parts ) ); 49 | } 50 | 51 | public function register() { 52 | $single = ucwords( $this->single ); 53 | $plural = ucwords( $this->plural ); 54 | $labels = array( 55 | 'name' => $plural, 56 | 'singular_name' => $single, 57 | 'add_new' => 'Add New', 58 | 'add_new_item' => 'Add New ' . $single, 59 | 'edit_item' => 'Edit ' . $single, 60 | 'new_item' => 'New ' . $single, 61 | 'all_items' => 'All ' . $plural, 62 | 'view_item' => 'View ' . $single, 63 | 'search_items' => 'Search ' . $plural, 64 | 'not_found' => 'No ' . $plural . ' found', 65 | 'not_found_in_trash' => 'No ' . $plural . ' found in Trash', 66 | 'parent_item_colon' => '', 67 | 'menu_name' => $plural, 68 | ); 69 | 70 | $args = array( 71 | 'labels' => $labels, 72 | 'public' => true, 73 | 'publicly_queryable' => true, 74 | 'show_ui' => true, 75 | 'show_in_menu' => true, 76 | 'show_in_rest' => true, 77 | 'query_var' => true, 78 | 'rewrite' => array( 'slug' => self::get_post_type( '' ) ), 79 | 'capability_type' => 'post', 80 | 'has_archive' => true, 81 | 'hierarchical' => false, 82 | 'menu_position' => $this->menu_position, 83 | 'supports' => $this->supports, 84 | ); 85 | 86 | if ( $this->icon ) { 87 | $args['menu_icon'] = 'dashicons-' . $this->icon; 88 | } 89 | 90 | register_post_type( $this->type, array_merge( $args, $this->args ) ); 91 | } 92 | 93 | public function get_columns( $columns ) { 94 | return $columns; 95 | } 96 | 97 | public function render_columns( $column ) { 98 | } 99 | 100 | /** 101 | * Return a list of all published posts. 102 | * 103 | * @param null $limit 104 | * @param string $order 105 | * 106 | * @return array 107 | */ 108 | public static function all( $limit = null, $order = 'DESC' ) { 109 | $limit = is_null( $limit ) ? - 1 : $limit; 110 | 111 | $query = new \WP_Query( array( 112 | 'post_type' => static::get_post_type(), 113 | 'posts_per_page' => $limit, 114 | 'post_status' => 'publish', 115 | 'orderby' => 'post_date', 116 | 'order' => $order, 117 | ) ); 118 | 119 | return static::get_posts( $query ); 120 | } 121 | 122 | protected static function get_model_class() { 123 | $parts = explode( '\\', get_called_class() ); 124 | $search = 'PostType'; 125 | $replace = 'Model'; 126 | $parts = array_map( function ( $part ) use ( $search, $replace ) { 127 | return $part == $search ? $replace : $part; 128 | }, $parts ); 129 | 130 | $model = implode( '\\', $parts ); 131 | 132 | if ( class_exists( $model ) ) { 133 | return $model; 134 | } 135 | 136 | return 'DeliciousBrains\\WPPostTypes\\Model\\Post'; 137 | } 138 | 139 | protected static function get_posts( $query ) { 140 | $posts = $query->get_posts(); 141 | foreach ( $posts as $key => $post ) { 142 | $class = self::get_model_class(); 143 | $posts[ $key ] = new $class( $post ); 144 | } 145 | 146 | return $posts; 147 | } 148 | 149 | /** 150 | * Redirect single and archive pages for post type to the homepage 151 | */ 152 | public function redirect_post_type_pages() { 153 | if ( ! is_singular( $this->type ) && ! is_post_type_archive( $this->type ) ) { 154 | return; 155 | } 156 | 157 | wp_redirect( home_url(), 301 ); 158 | exit; 159 | } 160 | 161 | /** 162 | * Suppress post type from Yoast sitemap. 163 | * 164 | * @param bool $value 165 | * @param string $post_type 166 | * 167 | * @return bool 168 | */ 169 | public function exclude_post_type_from_site_map( $value, $post_type ) { 170 | if ( $this->type === $post_type ) { 171 | return true; 172 | } 173 | 174 | return $value; 175 | } 176 | } --------------------------------------------------------------------------------