New! TypeRocket v5 is now available. See docs.
Access all of Typerocket. Get Pro.
Models
( v4 )
- # About Models
- # Making a Model
- # Command
- # Doc Example
- # Model Class Template Code
- # Base Model
- # Post Model
- # Term Model
- # Basic Examples
- # Fillable
- # Guard
- # Cast
- # Format
- # Query Engine
- # Finding
- # Get
- # Get Properties
- # Where
- # Grouped Where
- # Where Meta
- # Take: Limit and Offset
- # Order By
- # Date Time
- # Create
- # Update
- # Save
- # Delete
- # Count
- # Select
- # Get ID and Set Primary Key
- # Router Injection
- # Relationships
- # Accessors
- # With: Eager Loading
- # Multiple Relationships
- # Scoping
- # toArray and toJson
- # Importing Relationshsips
- # Post Type Models
About Models
Models are object representations of database tables and how you want them to behave. For example, if you have a table called wp_docs
with the columns id
(primary), version
, title
, json
, created_at
, modified_at
, and slug
you can create a model to access and manipulate it.
Making a Model
To create a model use the galaxy command make:model
. When making a model with the CLI it can use one of three directives:
-
base
- A model that represents any table in the database. -
post
- A model that represents a post type. -
term
- A model that represents a taxonomy term.
Both post
and term
are special and should be used with care. In many cases it is best to use the WordPress functions to deal with posts and terms so the hooks of other plugins are fired as expected.
The base
directive is the most useful and is for making a model that is connected to custom tables.
Note: The Galaxy command-line tool is not available if you are using the official TypeRocket Framework 4 WordPRess plugin.
Command
Here is the syntax for making a model
make:model [-c|--controller] [--] <directive> <name> [<id>]
Doc Example
To create a Doc
model class, representing the table from the about section, run the the command:
php galaxy make:model base Doc
Here is the class it creates:
<?php
namespace App\Models;
use App\Results\Docs;
use TypeRocket\Models\Model;
class Doc extends Model
{
protected $resource = 'docs';
}
Model Class Template Code
There are three model classes you can work with: Model
, WPPost
, and WPTerm
. It is best to use the Galaxy CLI to produce the template code for each of these types. However, you may want to make your models manually.
When making your models manually locate them in the app/Models
directory.
Base Model
php galaxy make:model base Doc
Will produce app/Models/Doc.php
,
<?php
namespace App\Models;
use \TypeRocket\Models\Model;
class Doc extends Model
{
protected $resource = 'docs';
}
Post Model
php galaxy make:model post Doc
Will produce app/Models/Doc.php
,
<?php
namespace App\Models;
use \TypeRocket\Models\WPPost;
class Doc extends WPPost
{
protected $postType = 'doc';
}
Term Model
php galaxy make:model term Version
Will produce app/Models/Version.php
,
<?php
namespace App\Models;
use \TypeRocket\Models\WPTerm;
class Version extends WPTerm
{
protected $taxonomy = 'version';
}
Basic Examples
You can query the database and retrieve the model you want. When a list of multiple items is returned a Results
collection is returned.
$docs = (new Doc())->where('version', 'v4')->findAll()->get();
You can also get a single item where the version is not v4
.
$doc = (new Doc())->where('version', '!=' ,'v4')->first();
And, where the ID is greater than 10.
$doc = new Doc;
$doc->where('version', '!=' ,'v4')
$doc->where('id', '>' , 10);
$doc->first();
Or, a single Doc
by ID.
$doc = (new Doc())->findById(1);
Fillable
To make limit what fields can be saved without be explicitly set use the $fillable
property
class Doc extends Model
{
// ...
protected $fillable = [
'title',
'json',
'version',
'slug'
];
}
Guard
To make restrict a set of fields that can not be saved without be explicitly set use the $guard
property
class Doc extends Model
{
// ...
protected $guard = [
'id',
'created_at',
'modified_at'
];
}
Cast
To cast a properties value when it is being accessed use the $cast
property.
class Doc extends Model
{
// ...
protected $cast = [
'id' => 'int', // cast to integer
'json' => 'array', // cast a json string to an array
];
}
Format
To format a models values before it is saved use the $format
property. Format can be used to sanitize data being saved to the database.
class Doc extends Model
{
// ...
protected $format = [
'slug' => 'sanitize_title',
'version' => 'static::sanitizeVersion'
];
public static function sanitizeVersion( $value ) {
return 'v' . filter_var($value, FILTER_SANITIZE_NUMBER_INT);
}
}
Query Engine
To create a model for manipulating tables rows create a new instance of the Model
you want to work with.
$doc = new Doc;
Finding
There are four find methods models can use: findById
, findAll
, findOrDie
, and findFirstWhereOrDie
.
To get one item by its primary column ID:
$doc->findById(1);
To get a list of all records:
$doc->findAll()->get();
To get a list of all records with specific IDs:
$doc->findAll([1,2,3])->get();
To get one record by its primary column ID or die with wp_die
:
$doc->findOrDie(1);
To get the first record:
$doc->first();
To get the first record by where the version is v4
or die with wp_die
:
$doc->findFirstWhereOrDie('version', 'v4');
Get
The get()
method queries the database. If more than one result is queried a results collection is returned.
$result = $doc->select('id')->take(10)->get();
Get Properties
The getProperties()
method returns all the properties, also called fields, of a model.
$fields = (new \App\Models\Post())->first()->getProperties();
Where
There are two where method for models where
and orWhere
. These methods can be chained any way you like.
$doc->where('version', '!=' ,'v4')->orWhere('id', 10);
$doc->first();
And,
$doc->where('version', '!=' ,'v4')->where('id', 10);
$doc->first();
Grouped Where
You can also group where clauses.
$where = [
[
'column' => 'ID',
'operator' => '=',
'value' => 1
],
'OR',
[
'column' => 'ID',
'operator' => '=',
'value' => '2'
]
];
$doc->where($where);
Where Meta
Models that have meta tables (WPPost
, WPUser
, WPComment
, and WPTerm
) can use the method whereMeta()
. whereMeta()
allows you to query the meta table of the model by the meta_key
and meta_value
.
// short syntax for where feature meta value is not null
(new Post)->whereMeta('feature')
// grouped meta query
(new Post)
->whereMeta([
[
'column' => 'feature',
'operator' => '!=',
'value' => null
],
'AND',
[
'column' => 'video_url',
'operator' => '!=',
'value' => null
]
])
->get();
// multiple meta queries
(new Post)
->whereMeta('video_url', 'like', 'https://facebook.com%')
->whereMeta('video_url', 'like', 'https://youtube.com%', 'OR')
->get();
Take: Limit and Offset
To take a certain scope of items use the take()
method. This method takes two arguments:
-
$limit
- The number of items to take. -
$offset
- The number of items to offset.
$result = $doc->findAll()->take(10, 10)->get();
Order By
You can set the ordering of results with orderBy()
. this method takes two arguments:
-
$column
- The column to order by, primary key is the default. -
$direction
- The direction or the orderingASC
(default) andDESC
.
$result = $doc->findAll()->orderBy('version', 'DESC')->get();
Date Time
In some cases you will want to get the current date time for the MySQL database. You can use the getDateTime()
method to access the current time in MySQL format.
$time = $doc->getDateTime();
Create
You can save any property explicitly set. Explicitly set properties ignore $guard
and $fillable
. To create a new row instance a model and explicitly set the values of the columns.
$doc = new Doc;
$doc->title = 'My Title';
$doc->json = ['key'=> 'value'];
$doc->version = 'v4';
$doc->slug = $doc->title;
$doc->created_at = $doc->getDateTime();
$doc->modified_at = $doc->created_at;
$doc->create();
Update
If your query has one result like with findById()
you can save any property explicitly set.
$doc = (new Doc)->findById(1);
$doc->json = ['key'=> 'new value'];
$doc->modified_at = $doc->created_at;
$doc->update();
Save
The save()
method will detect if a models primary key is set. If the key is set it will run the update()
method and if not it will run the create()
method.
Delete
If your query has one result like with findById()
you can delete it.
$doc = (new Doc)->findById(1);
$doc->delete();
You can also bulk delete items.
(new Doc)->delete([1,2,3]);
Count
You can run the count()
method on a model to get the number of items.
$number = $doc->findAll()->count();
Select
You can use the select()
method get return specific columns only.
$doc = (new Doc)->select('id', 'title')->first();
Get ID and Set Primary Key
The ID column is not always id
. To access the primary key column use the getID()
method. For example, in a model called Member
the primary key might be the users email address.
<?php
namespace App\Models;
use App\Results\Docs;
use TypeRocket\Models\Model;
class Member extends Model
{
protected $resource = 'members';
protected $idColumn = 'email';
}
Then,
$email = (new Member)->first()->getID();
Router Injection
If you are using custom routes and using controller injection you can set the automatic router injection column to something other than the primary ID by defining the getRouterInjectionColumn()
method in the model class.
class Doc extends Model
{
// ...
public function getRouterInjectionColumn()
{
return 'slug';
}
}
For the custom route,
// routes.php
tr_route()->get()->match('/docs/(.+)/', ['slug'])->do('slug@Doc');
Using the DocController
method slug
,
<?php
namespace App\Controllers;
use TypeRocket\Controllers\Controller;
class DocController extends Controller
{
function slug( \App\Models\Doc $doc, $slug ) {
return tr_view('docs.view', compact('doc'))->setTitle($doc->title);
}
}
Relationships
If you want to assign relationships to models view the relationships documentation.
Accessors
You can now implement Laravel-style accessors on models.
use \TypeRocket\Utility\Sanitize;
class Doc extends Model
{
// ...
public function getSlugProperty()
{
return Sanitize::dash($this->name);
}
}
You now have access to a slug
property on the Doc
model.
$doc = new Doc;
echo $doc->slug;
To understand how this works, have a look at the mutateProperty
function on the Model
class:
protected function mutateProperty($key, $value)
{
return $this->{'get'.Str::camelize($key).'Property'}($value);
}
You can see that the bit between get
and Property
becomes the property name. Within the function, the property is Pascal case. When getting the value, the property is a snake case. So $doc->getTheThingProperty()
becomes $doc->the_thing
. This allows you to mutate values from the database on retrieval, cleans up your constructors and property declarations, and removes the need to keep calling getter methods with long names
With: Eager Loading
You can eager load related models associated with a model using the with()
method. With method takes a single string
argument. The string
value must be the method name used for the model's relationship.
For example, the Post
model might have this relationship method:
public function meta() {
return $this->hasMany( WPPostMeta::class, 'post_id' );
}
You could eager load the associated WPPostMeta
models using the meta()
method by passing a string of 'meta'
to the with()
method of the Post
model before getting the results.
(new \App\Models\Post)->with('meta')->findById(1);
(new \App\Models\Post)->published()->with('meta')->get();
You can also use dot syntax to eager load deeply into your models using the same pattern. For example, say we have a model named MyCustomModel
and it has an author
relationship and the author
has a posts
relationship. You can deeply eager load by passing the string 'author.posts'
to the with()
method of the MyCustomModel
model.
(new \App\Models\MyCustomModel)->with('author.posts')->findById(1);
Multiple Relationships
You can eager load multiple relationships by passing an array to the with
method.
(new \App\Models\MyCustomModel)
->with(['author.posts', 'author.videos', 'location'])
->findById(1);
Scoping
You can scope eager loaded relationships by passing a callable to the with
method using an array and the key as the relationship name.
$pages = (new App\Models\Page)
->published()
->with(['meta' => function($query) {
return $query->where('meta_key', 'feature');
}])
->get();
Limiting the number of items eager loaded is not yet possible. If you need this feature you can create your own eager loading system until WordPress adds support for MySQL 8.
Note: You can not limit the number of items on eager loaded relationships using take
or another method. MySQL 8 has support for ROW_NUMBER
which would make this possible but WordPress does not support MySQL 8.
toArray and toJson
Convert modelt to an array
or json
.
$model->toArray();
$model->toJson();
Importing Relationshsips
When using eager loading toArray()
and toJson
will also import the loaded relationships as a part of the return value.
$machines = (new \App\Models\Manufacturer)
->with([
'machines.machineType',
'machines.industryType',
'machines.machineConfiguration'
])
->findById(1)->get();
$machines->toArray();
$machines->toJson();
Post Type Models
At times will want to access the WP_Post
object using a WPPost
model without making an extra query to the database. You can now do this using the WP_Post()
method on any post type model.
Here is an advanced usage example on how this can be used to optimize your database performance.
<?php
namespace App\Models;
use TypeRocket\Models\WPPost;
class Symbol extends WPPost
{
protected $postType = 'post';
public function getLinkProperty()
{
return get_the_permalink($this->WP_Post());
}
public function getFeaturedImageProperty()
{
$id = !empty($this->meta->_thumbnail_id) ? $this->meta->_thumbnail_id : 0;
return wp_get_attachment_image($id);
}
}
On the Frontend:
<?php
$symbol_model = new \App\Models\Symbol;
/**
* Meta is eager loaded and post meta cache is updated.
*/
$symbols = $symbol_model->with('meta')->published()->whereMeta('highlight', '=', '1')->get();
?>
<?php if($symbols) : foreach ($symbols as $symbol) :
/**
* Using accessors the permalink and featured image are loaded
* without overly querying the database.
*/
?>
<a class="col-3" href="<?= $symbol->link; ?>">
<?= $symbol->featured_image; ?>
</a>
<?php endforeach; endif; ?>
Found a typo? Something is wrong in this documentation? Fork and edit it!