Mass Assignment Laravel 4 Video

Eloquent ORM

Introduction

The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table.

Before getting started, be sure to configure a database connection in .

Basic Usage

To get started, create an Eloquent model. Models typically live in the directory, but you are free to place them anywhere that can be auto-loaded according to your file.

Defining An Eloquent Model

Note that we did not tell Eloquent which table to use for our model. The lower-case, plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Eloquent will assume the model stores records in the table. You may specify a custom table by defining a property on your model:

Note: Eloquent will also assume that each table has a primary key column named . You may define a property to override this convention. Likewise, you may define a property to override the name of the database connection that should be used when utilizing the model.

Once a model is defined, you are ready to start retrieving and creating records in your table. Note that you will need to place and columns on your table by default. If you do not wish to have these columns automatically maintained, set the property on your model to .

Retrieving All Models

Retrieving A Record By Primary Key

Note: All methods available on the query builder are also available when querying Eloquent models.

Retrieving A Model By Primary Key Or Throw An Exception

Sometimes you may wish to throw an exception if a model is not found, allowing you to catch the exceptions using an handler and display a 404 page.

To register the error handler, listen for the

Querying Using Eloquent Models

Eloquent Aggregates

Of course, you may also use the query builder aggregate functions.

If you are unable to generate the query you need via the fluent interface, feel free to use :

Chunking Results

If you need to process a lot (thousands) of Eloquent records, using the command will allow you to do without eating all of your RAM:

The first argument passed to the method is the number of records you wish to receive per "chunk". The Closure passed as the second argument will be called for each chunk that is pulled from the database.

Specifying The Query Connection

You may also specify which database connection should be used when running an Eloquent query. Simply use the method:

Mass Assignment

When creating a new model, you pass an array of attributes to the model constructor. These attributes are then assigned to the model via mass-assignment. This is convenient; however, can be a serious security concern when blindly passing user input into a model. If user input is blindly passed into a model, the user is free to modify any and all of the model's attributes. For this reason, all Eloquent models protect against mass-assignment by default.

To get started, set the or properties on your model.

Defining Fillable Attributes On A Model

The property specifies which attributes should be mass-assignable. This can be set at the class or instance level.

In this example, only the three listed attributes will be mass-assignable.

Defining Guarded Attributes On A Model

The inverse of is , and serves as a "black-list" instead of a "white-list":

Note: When using , you should still never pass or any raw array of user controlled input into a or method, as any column that is not guarded may be updated.

Blocking All Attributes From Mass Assignment

In the example above, the and attributes may not be mass assigned. All other attributes will be mass assignable. You may also block all attributes from mass assignment using the guard property:

Insert, Update, Delete

To create a new record in the database from a model, simply create a new model instance and call the method.

Saving A New Model

Note: Typically, your Eloquent models will have auto-incrementing keys. However, if you wish to specify your own keys, set the property on your model to .

You may also use the method to save a new model in a single line. The inserted model instance will be returned to you from the method. However, before doing so, you will need to specify either a or attribute on the model, as all Eloquent models protect against mass-assignment.

After saving or creating a new model that uses auto-incrementing IDs, you may retrieve the ID by accessing the object's attribute:

Setting The Guarded Attributes On The Model

Using The Model Create Method

Updating A Retrieved Model

To update a model, you may retrieve it, change an attribute, and use the method:

Saving A Model And Relationships

Sometimes you may wish to save not only a model, but also all of its relationships. To do so, you may use the method:

You may also run updates as queries against a set of models:

Note: No model events are fired when updating a set of models via the Eloquent query builder.

Deleting An Existing Model

To delete a model, simply call the method on the instance:

Deleting An Existing Model By Key

Of course, you may also run a delete query on a set of models:

Updating Only The Model's Timestamps

If you wish to simply update the timestamps on a model, you may use the method:

Soft Deleting

When soft deleting a model, it is not actually removed from your database. Instead, a timestamp is set on the record. To enable soft deletes for a model, apply the to the model:

To add a column to your table, you may use the method from a migration:

Now, when you call the method on the model, the column will be set to the current timestamp. When querying a model that uses soft deletes, the "deleted" models will not be included in query results.

Forcing Soft Deleted Models Into Results

To force soft deleted models to appear in a result set, use the method on the query:

The method may be used on a defined relationship:

If you wish to only receive soft deleted models in your results, you may use the method:

To restore a soft deleted model into an active state, use the method:

You may also use the method on a query:

Like with , the method may also be used on relationships:

If you wish to truly remove a model from the database, you may use the method:

The method also works on relationships:

To determine if a given model instance has been soft deleted, you may use the method:

Timestamps

By default, Eloquent will maintain the and columns on your database table automatically. Simply add these columns to your table and Eloquent will take care of the rest. If you do not wish for Eloquent to maintain these columns, add the following property to your model:

Disabling Auto Timestamps

Providing A Custom Timestamp Format

If you wish to customize the format of your timestamps, you may override the method in your model:

Query Scopes

Defining A Query Scope

Scopes allow you to easily re-use query logic in your models. To define a scope, simply prefix a model method with :

Utilizing A Query Scope

Dynamic Scopes

Sometimes you may wish to define a scope that accepts parameters. Just add your parameters to your scope function:

Then pass the parameter into the scope call:

Global Scopes

Sometimes you may wish to define a scope that applies to all queries performed on a model. In essence, this is how Eloquent's own "soft delete" feature works. Global scopes are defined using a combination of PHP traits and an implementation of .

First, let's define a trait. For this example, we'll use the that ships with Laravel:

If an Eloquent model uses a trait that has a method matching the naming convention, that trait method will be called when the Eloquent model is booted, giving you an opportunity to register a global scope, or do anything else you want. A scope must implement , which specifies two methods: and .

The method receives an query builder object, and is responsible for adding any additional clauses that the scope wishes to add. The method also receives a object and is responsible for reversing the action taken by . In other words, should remove the clause (or any other clause) that was added. So, for our , the methods look something like this:

Relationships

Of course, your database tables are probably related to one another. For example, a blog post may have many comments, or an order could be related to the user who placed it. Eloquent makes managing and working with these relationships easy. Laravel supports many types of relationships:

One To One

Defining A One To One Relation

A one-to-one relationship is a very basic relation. For example, a model might have one . We can define this relation in Eloquent:

The first argument passed to the method is the name of the related model. Once the relationship is defined, we may retrieve it using Eloquent's dynamic properties:

The SQL performed by this statement will be as follows:

Take note that Eloquent assumes the foreign key of the relationship based on the model name. In this case, model is assumed to use a foreign key. If you wish to override this convention, you may pass a second argument to the method. Furthermore, you may pass a third argument to the method to specify which local column that should be used for the association:

Defining The Inverse Of A Relation

To define the inverse of the relationship on the model, we use the method:

In the example above, Eloquent will look for a column on the table. If you would like to define a different foreign key column, you may pass it as the second argument to the method:

Additionally, you pass a third parameter which specifies the name of the associated column on the parent table:

One To Many

An example of a one-to-many relation is a blog post that "has many" comments. We can model this relation like so:

Now we can access the post's comments through the dynamic property:

If you need to add further constraints to which comments are retrieved, you may call the method and continue chaining conditions:

Again, you may override the conventional foreign key by passing a second argument to the method. And, like the relation, the local column may also be specified:

Defining The Inverse Of A Relation

To define the inverse of the relationship on the model, we use the method:

Many To Many

Many-to-many relations are a more complicated relationship type. An example of such a relationship is a user with many roles, where the roles are also shared by other users. For example, many users may have the role of "Admin". Three database tables are needed for this relationship: , , and . The table is derived from the alphabetical order of the related model names, and should have and columns.

We can define a many-to-many relation using the method:

Now, we can retrieve the roles through the model:

If you would like to use an unconventional table name for your pivot table, you may pass it as the second argument to the method:

You may also override the conventional associated keys:

Of course, you may also define the inverse of the relationship on the model:

Has Many Through

The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a model might have many through a model. The tables for this relationship would look like this:

Even though the table does not contain a column, the relation will allow us to access a country's posts via . Let's define the relationship:

If you would like to manually specify the keys of the relationship, you may pass them as the third and fourth arguments to the method:

Polymorphic Relations

Polymorphic relations allow a model to belong to more than one other model, on a single association. For example, you might have a photo model that belongs to either a staff model or an order model. We would define this relation like so:

Retrieving A Polymorphic Relation

Now, we can retrieve the photos for either a staff member or an order:

Retrieving The Owner Of A Polymorphic Relation

However, the true "polymorphic" magic is when you access the staff or order from the model:

The relation on the model will return either a or instance, depending on which type of model owns the photo.

Polymorphic Relation Table Structure

To help understand how this works, let's explore the database structure for a polymorphic relation:

The key fields to notice here are the and on the table. The ID will contain the ID value of, in this example, the owning staff or order, while the type will contain the class name of the owning model. This is what allows the ORM to determine which type of owning model to return when accessing the relation.

Many To Many Polymorphic Relations

Polymorphic Many To Many Relation Table Structure

In addition to traditional polymorphic relations, you may also specify many-to-many polymorphic relations. For example, a blog and model could share a polymorphic relation to a model. First, let's examine the table structure:

Next, we're ready to setup the relationships on the model. The and model will both have a relationship via a method:

The model may define a method for each of its relationships:

Querying Relations

Querying Relations When Selecting

When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the method:

You may also specify an operator and a count:

If you need even more power, you may use the and methods to put "where" conditions on your queries:

Dynamic Properties

Eloquent allows you to access your relations via dynamic properties. Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the (for one-to-many relationships) or (for one-to-one relationships) method. It will then be accessible via a dynamic property by the same name as the relation. For example, with the following model :

Instead of echoing the user's email like this:

It may be shortened to simply:

Note: Relationships that return many results will return an instance of the class.

Eager Loading

Eager loading exists to alleviate the N + 1 query problem. For example, consider a model that is related to . The relationship is defined like so:

Now, consider the following code:

This loop will execute 1 query to retrieve all of the books on the table, then another query for each book to retrieve the author. So, if we have 25 books, this loop would run 26 queries.

Thankfully, we can use eager loading to drastically reduce the number of queries. The relationships that should be eager loaded may be specified via the method:

In the loop above, only two queries will be executed:

Wise use of eager loading can drastically increase the performance of your application.

Of course, you may eager load multiple relationships at one time:

You may even eager load nested relationships:

In the example above, the relationship will be eager loaded, and the author's relation will also be loaded.

Eager Load Constraints

Sometimes you may wish to eager load a relationship, but also specify a condition for the eager load. Here's an example:

In this example, we're eager loading the user's posts, but only if the post's title column contains the word "first".

Of course, eager loading Closures aren't limited to "constraints". You may also apply orders:

Lazy Eager Loading

It is also possible to eagerly load related models directly from an already existing model collection. This may be useful when dynamically deciding whether to load related models or not, or in combination with caching.

Inserting Related Models

Attaching A Related Model

You will often need to insert new related models. For example, you may wish to insert a new comment for a post. Instead of manually setting the foreign key on the model, you may insert the new comment from its parent model directly:

In this example, the field will automatically be set on the inserted comment.

If you need to save multiple related models:

Associating Models (Belongs To)

When updating a relationship, you may use the method. This method will set the foreign key on the child model:

Inserting Related Models (Many To Many)

You may also insert related models when working with many-to-many relations. Let's continue using our and models as examples. We can easily attach new roles to a user using the method:

Attaching Many To Many Models

You may also pass an array of attributes that should be stored on the pivot table for the relation:

Of course, the opposite of is :

Both and also take arrays of IDs as input:

Using Sync To Attach Many To Many Models

You may also use the method to attach related models. The method accepts an array of IDs to place on the pivot table. After this operation is complete, only the IDs in the array will be on the intermediate table for the model:

Adding Pivot Data When Syncing

You may also associate other pivot table values with the given IDs:

Sometimes you may wish to create a new related model and attach it in a single command. For this operation, you may use the method:

In this example, the new model will be saved and attached to the user model. You may also pass an array of attributes to place on the joining table for this operation:

Touching Parent Timestamps

When a model another model, such as a which belongs to a , it is often helpful to update the parent's timestamp when the child model is updated. For example, when a model is updated, you may want to automatically touch the timestamp of the owning . Eloquent makes it easy. Just add a property containing the names of the relationships to the child model:

Now, when you update a , the owning will have its column updated:

Working With Pivot Tables

As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our object has many objects that it is related to. After accessing this relationship, we may access the table on the models:

Notice that each model we retrieve is automatically assigned a attribute. This attribute contains a model representing the intermediate table, and may be used as any other Eloquent model.

By default, only the keys will be present on the object. If your pivot table contains extra attributes, you must specify them when defining the relationship:

Now the and attributes will be accessible on our object for the model.

If you want your pivot table to have automatically maintained and timestamps, use the method on the relationship definition:

Deleting Records On A Pivot Table

To delete all records on the pivot table for a model, you may use the method:

Note that this operation does not delete records from the table, but only from the pivot table.

Updating A Record On A Pivot Table

Sometimes you may need to update your pivot table, but not detach it. If you wish to update your pivot table in place you may use method like so:

Defining A Custom Pivot Model

Laravel also allows you to define a custom Pivot model. To define a custom model, first create your own "Base" model class that extends . In your other Eloquent models, extend this custom base model instead of the default base. In your base model, add the following function that returns an instance of your custom Pivot model:

Collections

All multi-result sets returned by Eloquent, either via the method or a , will return a collection object. This object implements the PHP interface so it can be iterated over like an array. However, this object also has a variety of other helpful methods for working with result sets.

Checking If A Collection Contains A Key

For example, we may determine if a result set contains a given primary key using the method:

Collections may also be converted to an array or JSON:

If a collection is cast to a string, it will be returned as JSON:

Iterating Collections

Eloquent collections also contain a few helpful methods for looping and filtering the items they contain:

Filtering Collections

When filtering collections, the callback provided will be used as callback for array_filter.

Note: When filtering a collection and converting it to JSON, try calling the function first to reset the array's keys.

Applying A Callback To Each Collection Object

Sorting A Collection By A Value

Sorting A Collection By A Value

Returning A Custom Collection Type

Sometimes, you may wish to return a custom Collection object with your own added methods. You may specify this on your Eloquent model by overriding the method:

Accessors & Mutators

Defining An Accessor

Eloquent provides a convenient way to transform your model attributes when getting or setting them. Simply define a method on your model to declare an accessor. Keep in mind that the methods should follow camel-casing, even though your database columns are snake-case:

In the example above, the column has an accessor. Note that the value of the attribute is passed to the accessor.

Defining A Mutator

Mutators are declared in a similar fashion:

Date Mutators

By default, Eloquent will convert the and columns to instances of Carbon, which provides an assortment of helpful methods, and extends the native PHP class.

You may customize which fields are automatically mutated, and even completely disable this mutation, by overriding the method of the model:

When a column is considered a date, you may set its value to a UNIX timestamp, date string (), date-time string, and of course a / instance.

To totally disable date mutations, simply return an empty array from the method:

Model Events

Eloquent models fire several events, allowing you to hook into various points in the model's lifecycle using the following methods: , , , , , , , , , .

Whenever a new item is saved for the first time, the and events will fire. If an item is not new and the method is called, the / events will fire. In both cases, the / events will fire.

Cancelling Save Operations Via Events

If is returned from the , , , or events, the action will be cancelled:

Setting A Model Boot Method

Eloquent models also contain a static method, which may provide a convenient place to register your event bindings.

Model Observers

To consolidate the handling of model events, you may register a model observer. An observer class may have methods that correspond to the various model events. For example, , , methods may be on an observer, in addition to any other model event name.

So, for example, a model observer might look like this:

You may register an observer instance using the method:

Converting To Arrays / JSON

Converting A Model To An Array

When building JSON APIs, you may often need to convert your models and relationships to arrays or JSON. So, Eloquent includes methods for doing so. To convert a model and its loaded relationship to an array, you may use the method:

Note that entire collections of models may also be converted to arrays:

Converting A Model To JSON

To convert a model to JSON, you may use the method:

Returning A Model From A Route

Note that when a model or collection is cast to a string, it will be converted to JSON, meaning you can return Eloquent objects directly from your application's routes!

Hiding Attributes From Array Or JSON Conversion

Sometimes you may wish to limit the attributes that are included in your model's array or JSON form, such as passwords. To do so, add a property definition to your model:

Note: When hiding relationships, use the relationship's method name, not the dynamic accessor name.

Alternatively, you may use the property to define a white-list:

Occasionally, you may need to add array attributes that do not have a corresponding column in your database. To do so, simply define an accessor for the value:

Once you have created the accessor, just add the value to the property on the model:

Once the attribute has been added to the list, it will be included in both the model's array and JSON forms. Attributes in the array respect the and configuration on the model.

Early in 2012, a developer, named Egor Homakov, took advantage of a security hole at Github (a Rails app) to gain commit access to the Rails project.

His intent was mostly to point out a common security issue with many Rails apps that results from a feature, known as mass assignment (and did so rather loudly). In this article, we'll review what mass assignment is, how it can be a problem, and what you can do about it in your own applications.


What is Mass Assignment?

To begin, let's first take a look at what mass assignment means, and why it exists. By way of an example, imagine that we have the following class in our application:

# Assume the following fields: [:id, :first, :last, :email] class User < ActiveRecord::Base end

Mass assignment allows us to set a bunch of attributes at once:

attrs = {:first => "John", :last => "Doe", :email => "john.doe@example.com"} user = User.new(attrs) user.first #=> "John" user.last #=> "Doe" user.email #=> "john.doe@example.com"

Without the convenience of mass assignment, we'd have to write an assignment statement for each attribute to achieve the same result. Here's an example:

attrs = {:first => "John", :last => "Doe", :email => "john.doe@example.com"} user = User.new user.first = attrs[:first] user.last = attrs[:last] user.email = attrs[:email] user.first #=> "John" user.last #=> "Doe" user.email #=> "john.doe@example.com"

Obviously, this can get tedious and painful; so we bow at the feet of laziness and say, yes yes, mass assignment is a good thing.


The (Potential) Problem With Mass Assignment

One problem with sharp tools is that you can cut yourself with them.

But wait! One problem with sharp tools is that you can cut yourself with them. Mass assignment is no exception to this rule.

Suppose now that our little imaginary application has acquired the ability to fire missiles. As we don't want the world to turn to ash, we add a boolean permission field to the model to decide who can fire missiles.

class AddCanFireMissilesFlagToUsers < ActiveRecord::Migration def change add_column :users, :can_fire_missiles, :boolean, :default => false end end

Let's also assume that we have a way for users to edit their contact information: this might be a form somewhere that is accessible to the user with text fields for the user's first name, last name, and email address.

Our friend John Doe decides to change his name and update his email account. When he submits the form, the browser will issue a request similar to the following:

PUT http://missileapp.com/users/42?user[first]=NewJohn&user[email]=john.doe@newemail.com

The action within the might look something like:

def update user = User.find(params[:id]) if user.update_attributes(params[:user]) # Mass assignment! redirect_to home_path else render :edit end end

Given our example request, the hash will look similar to:

{:id => 42, :user => {:first => "NewJohn", :email => "john.doe@newemail.com"} # :id - parsed by the router # :user - parsed from the incoming querystring

Now let's say that NewJohn gets a little sneaky. You don't necessarily need a browser to issue an HTTP request, so he writes a script that issues the following request:

PUT http://missileapp.com/users/42?user[can_fire_missiles]=true

Fields, like , , and , are quite easily guessable.

When this request hits our action, the call will see , and give NewJohn the ability to fire missiles! Woe has become us.

This is exactly how Egor Homakov gave himself commit access to the Rails project. Because Rails is so convention-heavy, fields like , , and are quite easily guessable. Further, if there aren't protections in place, you can gain access to things that you're not supposed to be able to touch.


How to Deal With Mass Assignment

So how do we protect ourselves from wanton mass assignment? How do we prevent the NewJohns of the world from firing our missiles with reckless abandon?

Luckily, Rails provides a couple tools to manage the issue: and .

: The BlackList

Using , you can specify which fields may never be mass-ly assignable:

class User < ActiveRecord::Base attr_protected :can_fire_missiles end

Now, any attempt to mass-assign the attribute will fail.

: The WhiteList

The problem with is that it's too easy to forget to add a newly implemented field to the list.

This is where comes in. As you might have guessed, it's the opposite of : only list the attributes that you want to be mass-assignable.

As such, we can switch our class to this approach:

class User < ActiveRecord::Base attr_accessible :first, :last, :email end

Here, we're explicitly listing out what can be mass-assigned. Everything else will be disallowed. The advantage here is that if we, say, add an flag to the model, it will automatically be safe from mass-assignment.

As a general rule, you should prefer to , as it helps you err on the side of caution.

Mass Assignment Roles

Rails 3.1 introduced the concept of mass-assignment "roles". The idea is that you can specify different and lists for different situations.

class User < ActiveRecord::Base attr_accessible :first, :last, :email # :default role attr_accessible :can_fire_missiles, :as => :admin # :admin role end user = User.new({:can_fire_missiles => true}) # uses the :default role user.can_fire_missiles #=> false user2 = User.new({:can_fire_missiles => true}, :as => :admin) user.can_fire_missiles #=> true

Application-wide Configuration

You can control mass assignment behavior in your application by editing the setting within the file.

If set to , mass assignment protection will only be activated for the models where you specify an or list.

If set to , mass assignment will be impossible for all models unless they specify an or list. Please note that this option is enabled by default from Rails 3.2.3 forward.

Strictness

Beginning with Rails 3.2, there is additionally a configuration option to control the strictness of mass assignment protection: .

If set to , it will raise an any time that your application attempts to mass-assign something it shouldn't. You'll need to handle these errors explicitly. As of v3.2, this option is set for you in the development and test environments (but not production), presumably to help you track down where mass-assignment issues might be.

If not set, it will handle mass-assignment protection silently - meaning that it will only set the attributes it's supposed to, but won't raise an error.


Rails 4 Strong Parameters: A Different Approach

Mass assignment security is really about handling untrusted input.

The Homakov Incident initiated a conversation around mass assignment protection in the Rails community (and onward to other languages, as well); an interesting question was raised: does mass assignment security belong in the model layer?

Some applications have complex authorization requirements. Trying to handle all special cases in the model layer can begin to feel clunky and over-complicated, especially if you find yourself plastering all over the place.

A key insight here is that mass assignment security is really about handling untrusted input. As a Rails application receives user input in the controller layer, developers began wondering whether it might be better to deal with the issue there instead of ActiveRecord models.

The result of this discussion is the Strong Parameters gem, available for use with Rails 3, and a default in the upcoming Rails 4 release.

Assuming that our missile application is bult on Rails 3, here's how we might update it for use with the stong parameters gem:

Add the gem

Add the following line to the Gemfile:

gem strong_parameters

Turn off model-based mass assignment protection

Within :

config.active_record.whitelist_attributes = false

Tell the models about it

class User < ActiveRecord::Base include ActiveModel::ForbiddenAttributesProtection end

Update the controllers

class UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) end end

Now, if you attempt something like , you'll get an error in your application. You must first call on the hash with the keys that are allowed for a specific action.

The advantage to this approach is that you must be explicit about which input you accept at the point that you're dealing with the input.

Note: If this was a Rails 4 app, the controller code is all we'd need; the strong parameters functionality will be baked in by default. As a result, you won't need the include in the model or the separate gem in the Gemfile.


Wrapping Up

Mass assignment can be an incredibly useful feature when writing Rails code. In fact, it's nearly impossible to write reasonable Rails code without it. Unfortunately, mindless mass assignment is also fraught with peril.

Hopefully, you're now equipped with the necessary tools to navigate safely in the mass assignment waters. Here's to fewer missiles!

0 Replies to “Mass Assignment Laravel 4 Video”

Lascia un Commento

L'indirizzo email non verrĂ  pubblicato. I campi obbligatori sono contrassegnati *