Completed
Branch master (8f5c59)
by Evan
02:25
created

Model::postTypeId()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 25
rs 8.5806
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
        if (isset(static::$postTypeIds[static::class])) {
182
            return static::$postTypeIds[static::class];
183
        }
184
185
        /**
186
         * Convert the class name to snake_case and cache on a static property
187
         * to prevent evaluating more than once.
188
         */
189
        $name = (new \ReflectionClass(static::class))->getShortName();
190
191
        /**
192
         * Adapted from Str::snake()
193
         * @link https://github.com/laravel/framework/blob/5.2/src/Illuminate/Support/Str.php
194
         */
195
        if (! ctype_lower($name)) {
196
            $name = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1_', $name));
197
        }
198
199
        return static::$postTypeIds[static::class] = $name;
200
    }
201
202
    /**
203
     * Meta API for this post
204
     *
205
     * @param  string $key  Meta key to retreive or empty to retreive all.
206
     *
207
     * @return object
208
     */
209
    public function meta($key = '')
210
    {
211
        $meta = new ObjectMeta('post', $this->id);
212
213
        if ($key) {
214
            return $meta->get($key);
215
        }
216
217
        return $meta;
218
    }
219
220
    /**
221
     * Send the post to the trash
222
     *
223
     * If trash is disabled, the post or page is permanently deleted.
224
     *
225
     * @return $this
226
     */
227
    public function trash()
228
    {
229
        if (wp_trash_post($this->id)) {
230
            $this->refresh();
231
        }
232
233
        return $this;
234
    }
235
236
    /**
237
     * Restore a post or page from the Trash
238
     *
239
     * @return $this
240
     */
241
    public function untrash()
242
    {
243
        if (wp_untrash_post($this->id)) {
244
            $this->refresh();
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * Permanently deletes the post and related objects
252
     *
253
     * When the post and page is permanently deleted, everything that is
254
     * tied to it is deleted also. This includes comments, post meta fields,
255
     * and terms associated with the post.
256
     *
257
     * @return $this
258
     */
259
    public function delete()
260
    {
261
        if (wp_delete_post($this->id, true)) {
262
            $this->refresh();
263
        }
264
265
        return $this;
266
    }
267
268
    /**
269
     * Refresh the post object from cache/database
270
     *
271
     * @return $this
272
     */
273
    public function refresh()
274
    {
275
        $this->post = WP_Post::get_instance($this->id);
276
277
        return $this;
278
    }
279
280
    /**
281
     * Update the post in the database
282
     *
283
     * @return $this
284
     */
285
    public function save()
286
    {
287
        if (! $this->id) {
288
            $result = wp_insert_post($this->post->to_array(), true);
289
        } else {
290
            $result = wp_update_post($this->post, true);
291
        }
292
293
        if (is_wp_error($result)) {
294
            throw new WP_ErrorException($result);
295
        }
296
297
        $this->id = (int) $result;
298
299
        return $this->refresh();
300
    }
301
302
    public static function all()
303
    {
304
        return static::query()->limit(-1);
305
    }
306
307
    /**
308
     * Get a new query builder for the model.
309
     *
310
     * @return Builder
311
     */
312
    public function newQuery()
313
    {
314
        return (new Builder(new WP_Query))->setModel($this);
315
    }
316
317
    /**
318
     * Create a new query builder instance for this model type.
319
     *
320
     * @return Builder
321
     */
322
    public static function query()
323
    {
324
        return (new static)->newQuery();
325
    }
326
327
    /**
328
     * Magic getter
329
     *
330
     * @param  string $property
331
     *
332
     * @return mixed
333
     */
334
    public function __get($property)
335
    {
336
        if (property_exists($this, $property)) {
337
            return $this->$property;
338
        }
339
340
        /**
341
         * WP_Post translates non-existent properties to single post meta get
342
         */
343
        return $this->post->$property;
344
    }
345
346
    /**
347
     * Magic setter
348
     *
349
     * @param string $property
350
     * @param mixed $value
351
     */
352
    public function __set($property, $value)
353
    {
354
        if (isset($this->post->$property)) {
355
            $this->post->$property = $value;
356
        }
357
    }
358
359
    /**
360
     * Handle dynamic static method calls on the model class.
361
     *
362
     * Proxies calls to direct method calls on a new instance
363
     *
364
     * @param string $method
365
     * @param array $arguments
366
     *
367
     * @return mixed
368
     */
369
    public static function __callStatic($method, array $arguments)
370
    {
371
        return call_user_func_array([new static, $method], $arguments);
372
    }
373
374
    /**
375
     * Handle dynamic method calls into the model.
376
     *
377
     * @param  string $method
378
     * @param  array $arguments
379
     *
380
     * @return mixed
381
     */
382
    public function __call($method, $arguments)
383
    {
384
        $query = $this->newQuery();
385
386
        return call_user_func_array([$query, $method], $arguments);
387
    }
388
389
}
390