Completed
Push — master ( 8f5c59...22573e )
by Evan
02:45
created

Model::getPostTypeFromName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 22
rs 9.2
1
<?php
2
3
namespace Silk\Post;
4
5
use stdClass;
6
use WP_Post;
7
use WP_Query;
8
use Silk\Query\Builder;
9
use Silk\Meta\ObjectMeta;
10
use Silk\Exception\WP_ErrorException;
11
use Silk\Post\Exception\PostNotFoundException;
12
use Silk\Post\Exception\ModelPostTypeMismatchException;
13
14
/**
15
 * @property-read $post
16
 * @property-read $id
17
 * All WP_Post properties are available via magic get/set on this instance
18
 * @property $ID
19
 * @property $comment_count
20
 * @property $comment_status
21
 * @property $filter
22
 * @property $guid
23
 * @property $menu_order
24
 * @property $ping_status
25
 * @property $pinged
26
 * @property $post_author
27
 * @property $post_content
28
 * @property $post_content_filtered
29
 * @property $post_date
30
 * @property $post_date_gmt
31
 * @property $post_excerpt
32
 * @property $post_mime_type
33
 * @property $post_modified
34
 * @property $post_modified_gmt
35
 * @property $post_name
36
 * @property $post_parent
37
 * @property $post_password
38
 * @property $post_status
39
 * @property $post_title
40
 * @property $post_type
41
 * @property $to_ping
42
 */
43
abstract class Model
44
{
45
    /**
46
     * The post
47
     * @var WP_Post
48
     */
49
    protected $post;
50
51
    /**
52
     * Post ID
53
     * @var int
54
     */
55
    protected $id;
56
57
    /**
58
     * Class name derived post type identifiers
59
     * @var array  className => post_type
60
     */
61
    protected static $postTypeIds = [];
62
63
    /**
64
     * The post type of the post this model wraps
65
     * @var string
66
     */
67
    const POST_TYPE = '';
68
69
70
    /**
71
     * Create a new instance
72
     *
73
     * @param WP_Post $post  Post object to model
74
     */
75
    public function __construct(WP_Post $post = null)
76
    {
77
        if (! $post) {
78
            $post = new WP_Post(new stdClass);
79
            $post->post_type = static::postTypeId();
80
        }
81
82
        $this->post = $post;
83
        $this->id   = $post->ID;
84
    }
85
86
    /**
87
     * Create a new instance from the given WP_Post object
88
     *
89
     * @param  WP_Post $post
90
     *
91
     * @return static
92
     */
93
    public static function fromWpPost(WP_Post $post)
94
    {
95
        if ($post->post_type !== static::postTypeId()) {
96
            throw new ModelPostTypeMismatchException(static::class, $post);
97
        }
98
99
        return new static($post);
100
    }
101
102
    /**
103
     * Create a new instance from a Post with the given ID
104
     *
105
     * @param  int|string $id  Post ID of post to create the instance from
106
     *
107
     * @return static
108
     */
109
    public static function fromID($id)
110
    {
111
        $post = WP_Post::get_instance($id);
112
113
        if (false === $post) {
114
            throw new PostNotFoundException("No post found with ID {$id}");
115
        }
116
117
        return static::fromWpPost($post);
118
    }
119
120
    /**
121
     * Create a new instance from a Post with the given slug
122
     *
123
     * @param  string $slug  the post slug
124
     *
125
     * @return static
126
     */
127
    public static function fromSlug($slug)
128
    {
129
        $found = static::whereSlug($slug)->limit(1)->results();
130
131
        if ($found->isEmpty()) {
132
            throw new PostNotFoundException("No post found with slug {$slug}");
133
        }
134
135
        return $found->first();
136
    }
137
138
    /**
139
     * Create a new instance from the global $post
140
     *
141
     * @return static
142
     */
143
    public static function fromGlobal()
1 ignored issue
show
Coding Style introduced by
fromGlobal uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
144
    {
145
        if (! $GLOBALS['post'] instanceof WP_Post) {
146
            throw new PostNotFoundException('Global $post not an instance of WP_Post');
147
        }
148
149
        return static::fromWpPost($GLOBALS['post']);
150
    }
151
152
    /**
153
     * Create a new post of the model's type
154
     *
155
     * @param  array $attributes
156
     *
157
     * @return static
158
     */
159
    public static function create($attributes = [])
160
    {
161
        $post = new WP_Post((object)
162
            collect($attributes)
163
                ->except('ID')
164
                ->put('post_type', static::postTypeId())
165
                ->all()
166
        );
167
168
        return static::fromWpPost($post)->save();
169
    }
170
171
    /**
172
     * Get the post type identifier for this model
173
     *
174
     * @return string post type identifier (slug)
175
     */
176
    public static function postTypeId()
177
    {
178
        if (static::POST_TYPE) {
179
            return static::POST_TYPE;
180
        }
181
182
        return self::getPostTypeFromName();
183
    }
184
185
    /**
186
     * Get the post type id from the class name
187
     *
188
     * @return string
189
     */
190
    protected static function getPostTypeFromName()
191
    {
192
        if (isset(static::$postTypeIds[static::class])) {
193
            return static::$postTypeIds[static::class];
194
        }
195
196
        /**
197
         * Convert the class name to snake_case and cache on a static property
198
         * to prevent evaluating more than once.
199
         */
200
        $name = (new \ReflectionClass(static::class))->getShortName();
201
202
        /**
203
         * Adapted from Str::snake()
204
         * @link https://github.com/laravel/framework/blob/5.2/src/Illuminate/Support/Str.php
205
         */
206
        if (! ctype_lower($name)) {
207
            $name = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1_', $name));
208
        }
209
210
        return static::$postTypeIds[static::class] = $name;
211
    }
212
213
    /**
214
     * Meta API for this post
215
     *
216
     * @param  string $key  Meta key to retreive or empty to retreive all.
217
     *
218
     * @return object
219
     */
220
    public function meta($key = '')
221
    {
222
        $meta = new ObjectMeta('post', $this->id);
223
224
        if ($key) {
225
            return $meta->get($key);
226
        }
227
228
        return $meta;
229
    }
230
231
    /**
232
     * Send the post to the trash
233
     *
234
     * If trash is disabled, the post or page is permanently deleted.
235
     *
236
     * @return $this
237
     */
238
    public function trash()
239
    {
240
        if (wp_trash_post($this->id)) {
241
            $this->refresh();
242
        }
243
244
        return $this;
245
    }
246
247
    /**
248
     * Restore a post or page from the Trash
249
     *
250
     * @return $this
251
     */
252
    public function untrash()
253
    {
254
        if (wp_untrash_post($this->id)) {
255
            $this->refresh();
256
        }
257
258
        return $this;
259
    }
260
261
    /**
262
     * Permanently deletes the post and related objects
263
     *
264
     * When the post and page is permanently deleted, everything that is
265
     * tied to it is deleted also. This includes comments, post meta fields,
266
     * and terms associated with the post.
267
     *
268
     * @return $this
269
     */
270
    public function delete()
271
    {
272
        if (wp_delete_post($this->id, true)) {
273
            $this->refresh();
274
        }
275
276
        return $this;
277
    }
278
279
    /**
280
     * Refresh the post object from cache/database
281
     *
282
     * @return $this
283
     */
284
    public function refresh()
285
    {
286
        $this->post = WP_Post::get_instance($this->id);
287
288
        return $this;
289
    }
290
291
    /**
292
     * Update the post in the database
293
     *
294
     * @return $this
295
     */
296
    public function save()
297
    {
298
        if (! $this->id) {
299
            $result = wp_insert_post($this->post->to_array(), true);
300
        } else {
301
            $result = wp_update_post($this->post, true);
302
        }
303
304
        if (is_wp_error($result)) {
305
            throw new WP_ErrorException($result);
306
        }
307
308
        $this->id = (int) $result;
309
310
        return $this->refresh();
311
    }
312
313
    public static function all()
314
    {
315
        return static::query()->limit(-1);
316
    }
317
318
    /**
319
     * Get a new query builder for the model.
320
     *
321
     * @return Builder
322
     */
323
    public function newQuery()
324
    {
325
        return (new Builder(new WP_Query))->setModel($this);
326
    }
327
328
    /**
329
     * Create a new query builder instance for this model type.
330
     *
331
     * @return Builder
332
     */
333
    public static function query()
334
    {
335
        return (new static)->newQuery();
336
    }
337
338
    /**
339
     * Magic getter
340
     *
341
     * @param  string $property
342
     *
343
     * @return mixed
344
     */
345
    public function __get($property)
346
    {
347
        if (property_exists($this, $property)) {
348
            return $this->$property;
349
        }
350
351
        /**
352
         * WP_Post translates non-existent properties to single post meta get
353
         */
354
        return $this->post->$property;
355
    }
356
357
    /**
358
     * Magic setter
359
     *
360
     * @param string $property
361
     * @param mixed $value
362
     */
363
    public function __set($property, $value)
364
    {
365
        if (isset($this->post->$property)) {
366
            $this->post->$property = $value;
367
        }
368
    }
369
370
    /**
371
     * Handle dynamic static method calls on the model class.
372
     *
373
     * Proxies calls to direct method calls on a new instance
374
     *
375
     * @param string $method
376
     * @param array $arguments
377
     *
378
     * @return mixed
379
     */
380
    public static function __callStatic($method, array $arguments)
381
    {
382
        return call_user_func_array([new static, $method], $arguments);
383
    }
384
385
    /**
386
     * Handle dynamic method calls into the model.
387
     *
388
     * @param  string $method
389
     * @param  array $arguments
390
     *
391
     * @return mixed
392
     */
393
    public function __call($method, $arguments)
394
    {
395
        $query = $this->newQuery();
396
397
        return call_user_func_array([$query, $method], $arguments);
398
    }
399
400
}
401