name: inverse layout: true class: center, middle, inverse --- # Eloquent ORM ### Packaged with Laravel --- ## An Active Record Implementation ??? Active Record is an implementation of the Object Relational Mapper technique of accessing a relational database from an object-oriented language In other words, it's a database interaction abstraction. Active Record differs from Data Mapper in that an AR model generally represents one row in the database. --- layout: false ## What is it actually? ### Technical Jargon * ORM * Object Relational Mapper * Active Record * Data Mapper -- ### Keeping it Real - You have an object that is representative of your database tables and rows - A way of representing your database values with an object you can easily use in your applications --- ### Database Interaction #### Raw SQL ``` SELECT * FROM posts LIMIT 1 ``` -- #### PDO Example ```php $query=$db->prepare("Select * from ? LIMIT 1"); $query->excute(array($myinsecuredata)); while($row=$query->fetch(PDO::FETCH_OBJ)) { echo $row->yourcolumnname; } ``` -- #### Eloquent Equivalent ```php $post = Post::first(); $post->columnName; ``` ??? To be fair, you would write something around the PDO example to make it easier to work with. You shouldn't write all of that in your view code. Which is really what Eloquent has basically already done for you. --- ## Define a Post Model ```php You can generate models with Laravel Artisan (a command line tool). ``` $ php artisan make:model Post ``` --- # Create a Post ```php $post = [ 'title' => 'My Killer Post', 'teaser' => 'Guy finds hole in ground, you wont believe what he found inside', 'content' => 'Really he didnt find anything, this is click-bait, come on.' ]; Post::create($post); ``` -- ```php Post::create([ 'title' => 'My Killer Post', 'teaser' => 'Guy finds hole in ground, you wont believe what he found inside', 'content' => 'Really he didnt find anything, this is click-bait, come on.' ]); ``` -- ```php Post::create($request->all()); ``` --- ## Nobody Expects the MassAssignmentException ![](images/mass-assignment.png) .center[![](images/massassignmentmeme.jpg)] --- ## Update the $fillable property to allow us to assign values ```php all()); $post->title; // My Killer Post $post->teaser; // Guy finds hole in ground, you wont believe what he found inside $post->content; // Really he didnt find anything, this is click-bait, come on. ``` -- ## Now Marketing wants to change something...of course. ```php $post = Post::find($id); $post->title = 'This post will blow your mind'; $post->save(); ``` ```php Post::find($id)->update(['title' => 'People are dying reading this']); ``` ```php Post::find($id)->update($request->all()); ``` `saveMany(); // pass an array of models` --- ## OMG, delete the evidence ```php Post::find($id)->delete(); ``` -- ## But also you can do this: ```php Post::destroy([1, 2, 3]); // an array of ids you want to delete ``` --- ## Soft Deletes 1. In order to enable a model to be able to be soft deleted, you need to make sure your DB table has the `deleted_at` field. (Migrations allow this easily with `softDeletes()` function). 1. You need to use the SoftDeletes Trait ```php delete();` and `Post::destroy([$id]);` will mark posts as deleted instead of deleting the rows. -- Soft deleting has functions available like `forceDelete()`, `restore()`, `withTrashed()`, `onlyTrashed()`, `history()`, `trashed()` --- ## Everybody with me so far? Any quick questions? --- ## Reminder - This is what our Post model looks like: ```php Eloquent uses Inheritance to achieve this. --- ## How does it even know which table to use? By default Eloquent looks at the class name, `Post` in this case, it lowercases and pluralizes it; `posts` and looks for that in our DB. -- ## Want to change the table? No Problem. On your model add a `$table` property and set it to the table it needs to be. ```php protected $table = 'blogPosts'; ``` > The idea here is that it matches our natural language. > We have a `posts` table that contains posts. > Our model is a single `Post` which is the name of the class. --- ## Basic Queries ### All Records for a model ```php $posts = Post::all(); ``` -- ```php [ { id: 1, title: "People are dying reading this", content: "Really he didnt find anything, this is click-bait, come on.", published_at: "2016-04-10 00:08:09", author_id: 1, teaser: "Guy finds hole in ground, you wont believe what he found inside", created_at: "2016-04-10 22:03:58", updated_at: "2016-04-17 23:48:44" }, { id: 2, title: "Example Post", content: "Some Killer content", published_at: "2016-04-13 00:08:15", author_id: 2, teaser: "OMG, check this out.", created_at: "2016-04-13 00:07:04", updated_at: "2016-04-18 00:07:04" } ] ``` --- ## Basic Queries - Constraints ```php $posts = Post::where('author_id', '=', 1)->get(); ``` -- If operator is `'='` then you can omit it: ```php $posts = Post::where('author_id', 1)->get(); ``` Operator can be `>`, `>=`, `<`, `<=`, `!=`, `<>` or any other operator -- ```php $posts = Post::where('author_id', 1)->orderBy('published_at')->get(); $posts = Post::where('author_id', 1)->orderBy('published_at', 'DESC')->get(); // ASC is default so can be omitted as second argument ``` --- ## Eloquent is Chainable ```php $posts = Post::where('posts.published_at', '<', Carbon::now()) ->where('author_id', 1) ->join('users', 'users.id', '=', 'posts.author_id') ->select('posts.*', 'users.email') ->orderBy('posts.published_at', 'DESC') ->get(); ``` -- `leftJoin()`, `crossJoin()`, `union()`, `unionAll()`, `whereNull()`, `whereBetween()`, `whereIn()`, `whereNotIn()`, `whereNotNull()`, `orWhere()` and more You can also pass an array of `where` statements to `where()`. ```php Post::where([ 'author' => 1, 'published' => 1 ])->get(); ``` Safe to say, you can do most queries with the fluent Eloquent syntax --- ## What do you think...good code, bad code? ```php $posts = Post::where('posts.published_at', '<', Carbon::now()) ->where('author_id', 1) ->join('users', 'users.id', '=', 'posts.author_id') ->select('posts.*', 'users.email') ->get(); ``` -- I think it is good. This saves you a ton of code already and is easy to read. However, Eloquent can make this significantly better still. --- template: inverse # Good to Great --- ## Custom Scopes We can define a scope on our model, and then use it to make queries. Think of scope as "limit" or "where". On our Post model we will make a 'Published' scope: ```php where('published_at', '<=', \Carbon\Carbon::now()); } } ``` --- ## Using our Scope ```php $posts = Post::published() ->where('author_id', 1) ->join('users', 'users.id', '=', 'posts.author_id') ->select('posts.*', 'users.email') ->get(); ``` We can now call `published()` in our chain on our Post model. If we litter our code with `Post::where('published_at', '<=', \Carbon\Carbon::now())->get()` and we need to make a change, we have to go find all uses of this code and update them. Using a scope, we can easily update our app when changes happen. -- > Hmm, what to do now? How do we improve this further? > We could create more scopes, but what about that join? > Wouldn't it be cool if we could define the relationship between `posts` and `users` somehow? --- ## Relationships You can define relationships on models. Let's add one to our `Post` model. ```php where('published_at', '<=', \Carbon\Carbon::now()); } public function author() { return $this->belongsTo(User::class); } } ``` --- ## Accessing Relationships ```php $post = Post::published()->first(); $post->author->email; // testing@test.com ``` `$post->author` returns an instance of our User model allowing access to the `email` property. -- When we did this query: ```php $post = Post::published() ->where('author_id', 1) ->join('users', 'users.id', '=', 'posts.author_id') ->select('posts.*', 'users.email') ->first(); ``` `$post` contains the author email. That's a little weird. The Relationship style, we access the author as a property on our Post model, and the email as a property of the author/User. That makes sense. `$post->author->email` instead of `$post->email` --- ## Types of Relationships * One To One * `return $this->hasOne(Model::class);` * `return $this->belongsTo(Model::class);` * One To Many * `return $this->hasMany(Model::class);` * Many to Many * `return $this->belongsToMany(Model::class);` * Has Many Through * `return $this->hasManyThrough(Model::class, Connecting::class);` * Polymorphic Relations * Requires things to be set on multiple models. (See docs) --- ### Polymorphic Relations Polymorphic relations allow a model to belong to more than one other model on a single association. For example, imagine users of your application can "like" both posts and comments. Using polymorphic relationships, you can use a single likes table for both of these scenarios. First, let's examine the table structure required to build this relationship: Likes ![](images/favoritestable.png) --- ```php class Like extends Model { /** * Get all of the owning likeable models. */ public function likeable() { return $this->morphTo(); } } class Post extends Model { /** * Get all of the product's likes. */ public function likes() { return $this->morphMany('App\Like', 'likeable'); } } class Comment extends Model { /** * Get all of the comment's likes. */ public function likes() { return $this->morphMany('App\Like', 'likeable'); } } ``` --- ## Query Relations ```php $user = App\User::find(1); $user->posts()->where('active', 1)->get(); ``` -- ## Insert with Relationship ```php $user = App\User::find(1); // This will attribute the post to the user. $user->posts()->create($request->all()); ``` --- ## Relationship methods vs Dynamic Properties ### Calling a Relationship like a property When you call the relationship like a property, Eloquent returns a Collection. ```php $user = App\User::find(1); $user->posts; ``` ### Calling a Relationship like a method When you refer to the relationship as a method, Eloquent returns an instance of the Relationship. ```php $user = App\User::find(1); $user->posts(); ``` This means, when referencing like a METHOD, you can call additional constraints. As a PROPERTY, you would be manipulating a collection. --- ## first(), get(), all(), paginate() These execute the query, and return different things. #### first() Returns the object model for the given query. #### get() Returns a collection of items limited to a `$perPage` property on the model. #### all() Returns a collection of all items. #### paginate(10) Returns a collection of the first 10 items you are querying. ### Collections are a like arrays, but with additional features. --- ## Collections As mentioned, this are like super arrays. How many times have you written this: ```php $result = []; foreach ($family as $member) { $result[] = strtoupper($member); } return $result; ``` Now when you have a collection you can use `map()` ```php $collection->map(function($name) { return strtoupper($name); }); ``` ## Available Methods `all` `chunk` `collapse` `contains` `count` `diff` `each` `every` `filter` `first` `flatten` `flip` `forget` `forPage` `get` `groupBy` `has` `implode` `intersect` `isEmpty` `keyBy` `keys` `last` `map` `merge` `pluck` `pop` `prepend` `pull` `push` `put` `random` `reduce` `reject` `reverse` `search` `shift` `shuffle` `slice` `sort` `sortBy` `sortByDesc` `splice` `sum` `take` `toArray` `toJson` `transform` `unique` `values` `where` `whereLoose` `zip` --- # Eager Loading ## Relationships Make Multiple Queries In some cases, this won't matter, but sometimes you can end up with 100 queries on a page load without even realizing it. -- ## `with()` will consolidate queries ```php $post = Post::published()->with('author')->first(); ``` You can also define `$with` as a property on the model if you think you will always need the relationship. Like a User and a Profile. --- ## Additional Properties you can define ```php $primaryKey = 'id'; // In case your id column has a different name $timestamps = false; // If this model doesn't use timestamps. Default is true $incrementing = false; // Let's Eloquent know if your primaryKey is not incrementing like a UUID // The alternate to $fillable, you can set which fields are not Mass Assignable // (the rest are MA) $guarded = ['columnName']; // Generally, $fillable is preferred. Use either $fillable or $guarded, not both. // Makes any date fields an instance of Carbon $dates = ['published_at', 'created_at', 'updated_at']; $casts = [ 'is_admin' => 'boolean', 'published_at' => 'date' // returns a Carbon instance like `created_at` ]; ``` > Available cast types: `integer`, `real`, `float`, `double`, `string`, `boolean`, `object`, `array`, `collection`, `date`, `datetime`, and `timestamp`. > Additional properties: `$touches`, `$hidden`, `$visible`, `$appends` --- layout: true ## Accessors & Mutators --- Want a value formated a certain way every time you use it? Set an Accessor. On the model: ```php public function getFirstNameAttribute($value) { return ucfirst($value); } // DB: andy Access: $user->firstName Output: Andy ``` > You do not access these as `getFirstNameAttribute()`, you simply access the field like normal, now it will be formatted. --- Want a value formatted every time you store it? Set a Mutator. ```php public function setFirstNameAttribute($value) { $this->attributes['first_name'] = ucfirst($value); } // Input: andy DB: Andy ``` > `set` for Mutator and `get` for Accessor --- layout: false ## Custom Methods We could litter calls to the same code throughout our application, but updating that is going to be a pain. If you find yourself writing the same "where" clause or anything, consider writing a custom method that will keep your code clean and easy to read. #### Example ```php $user = User::where('username', $username)->first(); ``` This example is super easy, yes you can easily read this and figure out, this is an example though. And still, if you have this in your code like 20 times, and you need to update something about it, then you have to go find all the places you wrote this code. #### Instead make a custom method ```php // User Model public static function findByUsername($username) { return static::where('username', $username)->first; } ``` --- layout: false ### Now This: ```php $user = User::where('username', $username)->first(); ``` ### Would Be: ```php $user = User::findByUsername($username); ``` -- Now, when you come back to this code in 6 months, you will know exactly what is going on here. --- ## Events When an Eloquent model creates a record, updates, deletes and some other type of event...an event is fired. ### Available events to hook into `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored` You can have code execute each time one of these events are fired. So if you need a certain piece of data, or a notification fired. You can move that code out of your controller and have it be reusable. --- ## Wait wait there's more -- .center[![](images/Notime.jpg)] --- layout: false name: last-page template: inverse ## Andrew Huggins ### @andy_huggins ### andrewhuggins@gmail.com Slides: http://ahuggins.github.io/presentation-eloquent/ .footnote[ Slideshow created using http://github.com/gnab/remark ] ??? Mention Laracasts coupon code 30% off (if it still works)