Laravel — P37: Models $fillable, belongsTo and hasMany (CMP)

Mastering Models in Laravel

Our tables have been created and populated. We need to do some tweaking to our models in order to get them behaving like we want them to. Primarily, we need to make sure that data can be inserted and that relationships are setup. What kind of relationships? Remember that we have foreign id’s in a couple of our tables. We need to let Laravel know how to access data from the referenced tables.

Laravel — P36: Seeders and Factories (CMP)

Fillable

In order to add data inside of our tables when we submit our forms, we need to specify which fields are fillable. We could “cheat,” and quite a few developers do, by eliminating this check. To do that, you set the $guarded variable to an empty array. We won’t do that. We’ll specify which fields can be inserted.

PersonalCar

The PersonalCar contains all of the fields except a couple:

  • id
  • personal_car_brand_id
  • personal_car_model_id
  • created_at
  • updated_at

The idcreated_at, and updated_at fields are generated automatically through mysql. The foreign id’s will be inserted via relationships, so we don’t need to add those to the $fillable property.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCar extends Model
{
    use HasFactory;

    protected $fillable = [
        'year', 'is_manual', 'exterior_color', 'purchase_amount', 'current_value', 'sales_amount', 'date_purchased', 'date_sold'
    ];
}

PersonalCarBrand

Similarly, we add the fillable fields for the PersonalCarBrand.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCarBrand extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'slug',
    ];
}

PersonalCarModel

We’ll repeat the process for PersonalCarModel.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCarModel extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'slug',
    ];
}

Image

Remember the Image model? I know that we haven’t looked at it until now, but our cars will have images. We need to make sure those fields are fillable as well.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    use HasFactory;

    protected $fillable = [
        'url', 'alt',
    ];
}

Do we need to do anything for our image_personal_car pivot table? Not yet. That’ll be tackled when we look at relationships.

Relationships

We haven’t looked at any sort of relationships yet. I wanted to throw in a couple of relationships into the mix, but we’ll take a deep dive into relationships later. For now, let’s create our first relationship. Let’s tackle the personal_car_brand_id in our PersonalCar model.

BelongsTo

If we think about it, our PersonalCarBrand can have many PersonalCars. Our PersonalCar can’t have many PersonalCarBrands. Remember, we’re talking about one instance of the model. If I look at one of my cars, a 2007 Chevy Corvette, it belongs to one specific brand: Chevy. However, if I look at Chevy, I can see that the brand has two cars that are associated with it, since I have a 2003 Chevy Corvette as well (kind of obsessed with those cars). So Chevy hasMany cars associated with it, but my 2007 Chevy Corvette belongsTo one brand.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCar extends Model
{
    use HasFactory;

    protected $fillable = [
        'year', 'is_manual', 'exterior_color', 'purchase_amount', 'current_value', 'sales_amount', 'date_purchased', 'date_sold'
    ];

    public function brand()
    {
        return $this->belongsTo(PersonalCarBrand::class, 'personal_car_brand_id', 'id');
    }
}

We created a brand method. It returns a belongsTo relationship. It’s saying that the PersonalCar belongsTo a PersonalCarBrand. The second argument is the foreign key inside of our PersonalCar and the third argument is the key that we’re referencing in our PersonalCarBrand, in this case it’s id.

Let’s test and see if it works. Open tinker.

# php artisan tinker;
Psy Shell v0.11.9 (PHP 8.1.13 — cli) by Justin Hileman
Let’s run our first command that we are familiar with, just returning the first row.
> PersonalCar::first();

= App\Models\PersonalCar {#4722
    id: 1,
    year: "2022",
    personal_car_brand_id: 6,
    personal_car_model_id: 6,
    is_manual: 1,
    exterior_color: "itaque",
    purchase_amount: 21846038,
    current_value: 635120796,
    sales_amount: 0,
    date_purchased: "2014-01-18",
    date_sold: null,
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
  }

> 
In tinker, we can assign the model to a variable, like $car.
> $car = PersonalCar::first();

= App\Models\PersonalCar {#4504
    id: 1,
    year: "2022",
    personal_car_brand_id: 6,
    personal_car_model_id: 6,
    is_manual: 1,
    exterior_color: "itaque",
    purchase_amount: 21846038,
    current_value: 635120796,
    sales_amount: 0,
    date_purchased: "2014-01-18",
    date_sold: null,
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
  }

>
Next, let’s see how we can access our relationship. It’s just a method in our class. Let’s see what happens when we call our method.
> $car->brand();

= Illuminate\Database\Eloquent\Relations\BelongsTo {#4724}
> 
Not what we expected. That’s because calling the method with the parentheses calls the eloquent relationship and does not execute the code. To execute the code, we need to append first() or get().
> $car->brand()->first();

= App\Models\PersonalCarBrand {#4727
    id: 6,
    name: "Quidem",
    slug: "quidem",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
  }

> 
There is also a shortcut, so you don’t have to list out ->get() or ->first() each time and that’s to just get rid of the parentheses. You’ll see this type of syntax most often.
> $car->brand;

= App\Models\PersonalCarBrand {#4728
    id: 6,
    name: "Quidem",
    slug: "quidem",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
  }

> 

Now, if we wanted to look at the name of our car, we could simply go directly to the name property of our PersonalCarBrand model.

> $car->brand->name;

= "Quidem"

> 
Each time that we do this do though, we’re creating a new query. If we know that we’ll always need the brand, we can eager load it. You’ll do that by using the with() method and specifying the relationship that you want to eager load.
> $car = PersonalCar::with('brand')->first();

= App\Models\PersonalCar {#4718
    id: 1,
    year: "2022",
    personal_car_brand_id: 6,
    personal_car_model_id: 6,
    is_manual: 1,
    exterior_color: "itaque",
    purchase_amount: 21846038,
    current_value: 635120796,
    sales_amount: 0,
    date_purchased: "2014-01-18",
    date_sold: null,
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    brand: App\Models\PersonalCarBrand {#4738
      id: 6,
      name: "Quidem",
      slug: "quidem",
      created_at: "2023-01-20 20:37:11",
      updated_at: "2023-01-20 20:37:11",
    },
  }

> 

As you can see, our brand is already loaded.

Let’s repeat the process for our PersonalCarModel relationship.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCar extends Model
{
    use HasFactory;

    protected $fillable = [
        'year', 'is_manual', 'exterior_color', 'purchase_amount', 'current_value', 'sales_amount', 'date_purchased', 'date_sold'
    ];

    public function brand()
    {
        return $this->belongsTo(PersonalCarBrand::class, 'personal_car_brand_id', 'id');
    }

    public function model()
    {
        return $this->belongsTo(PersonalCarModel::class, 'personal_car_model_id', 'id');
    }
}
We’ll jump directly into eager loading the model for our test this time. Remember to restart tinker or it won’t work properly (CTRL+C).
> $car = PersonalCar::with(['brand', 'model'])->first();

[!] Aliasing 'PersonalCar' to 'App\Models\PersonalCar' for this Tinker session.
= App\Models\PersonalCar {#4721
    id: 1,
    year: "2022",
    personal_car_brand_id: 6,
    personal_car_model_id: 6,
    is_manual: 1,
    exterior_color: "itaque",
    purchase_amount: 21846038,
    current_value: 635120796,
    sales_amount: 0,
    date_purchased: "2014-01-18",
    date_sold: null,
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    brand: App\Models\PersonalCarBrand {#4727
      id: 6,
      name: "Quidem",
      slug: "quidem",
      created_at: "2023-01-20 20:37:11",
      updated_at: "2023-01-20 20:37:11",
    },
    model: App\Models\PersonalCarModel {#4731
      id: 6,
      name: "Vel",
      slug: "vel",
      created_at: "2023-01-20 20:37:11",
      updated_at: "2023-01-20 20:37:11",
    },
  }

> 

The with() method can accept a string if it’s just one relationship, or an array if there are multiple relationships that you’re trying to eager load.

$car = PersonalCar::with(['brand', 'model'])->first();

As you can see, we have all of our PersonalCar properties and both of our relationships loaded.

hasMany

What if we wanted to see all of the cars that are associated with our brands and models? We’ll need to setup hasMany relationships on those.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCarBrand extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'slug',
    ];

    public function cars()
    {
        return $this->hasMany(PersonalCar::class, 'personal_car_brand_id', 'id');
    }
}

The cars method states that the PersonalCarBrand hasMany cars. Those cars are stored in the PersonalCar model and the foreign id that links back to our PersonalCarBrand from PersonalCar is the personal_car_brand_id. It’s linked to the id on PersonalCarBrand.

> $brand = PersonalCarBrand::with(['cars'])->find(6);

[!] Aliasing 'PersonalCarBrand' to 'App\Models\PersonalCarBrand' for this Tinker session.
= App\Models\PersonalCarBrand {#4717
    id: 6,
    name: "Quidem",
    slug: "quidem",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    cars: Illuminate\Database\Eloquent\Collection {#4720
      all: [
        App\Models\PersonalCar {#4725
          id: 1,
          year: "2022",
          personal_car_brand_id: 6,
          personal_car_model_id: 6,
          is_manual: 1,
          exterior_color: "itaque",
          purchase_amount: 21846038,
          current_value: 635120796,
          sales_amount: 0,
          date_purchased: "2014-01-18",
          date_sold: null,
          created_at: "2023-01-20 20:37:11",
          updated_at: "2023-01-20 20:37:11",
        },
      ],
    },
  }

When we eager load our relationship, we see that the cars key contains an array of cars. In this instance, there’s only one car associated with this particular object (row 6). If we try another one, we can see that sometimes we get no results and other times we get multiple results.

> $brand = PersonalCarBrand::with(['cars'])->find(1);

= App\Models\PersonalCarBrand {#4726
    id: 1,
    name: "Dolorem",
    slug: "dolorem",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    cars: Illuminate\Database\Eloquent\Collection {#4730
      all: [],
    },
  }

> 
> $brand = PersonalCarBrand::with(['cars'])->find(7);

= App\Models\PersonalCarBrand {#4751
    id: 7,
    name: "Quam",
    slug: "quam",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    cars: Illuminate\Database\Eloquent\Collection {#4771
      all: [
        App\Models\PersonalCar {#4779
          id: 5,
          year: "1991",
          personal_car_brand_id: 7,
          personal_car_model_id: 10,
          is_manual: 0,
          exterior_color: "tempora",
          purchase_amount: 28457362,
          current_value: 720832497,
          sales_amount: 0,
          date_purchased: "1984-05-10",
          date_sold: null,
          created_at: "2023-01-20 20:37:11",
          updated_at: "2023-01-20 20:37:11",
        },
        App\Models\PersonalCar {#4777
          id: 8,
          year: "1974",
          personal_car_brand_id: 7,
          personal_car_model_id: 4,
          is_manual: 1,
          exterior_color: "tenetur",
          purchase_amount: 2236172,
          current_value: 779288418,
          sales_amount: 0,
          date_purchased: "1979-07-15",
          date_sold: null,
          created_at: "2023-01-20 20:37:11",
          updated_at: "2023-01-20 20:37:11",
        },
      ],
    },
  }

> 
We’ll need to repeat the same process for our PersonalCarModel if we find the need for it. I think it’s good practice to do it, so let’s do it.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PersonalCarModel extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'slug',
    ];

    public function cars()
    {
        return $this->hasMany(PersonalCar::class, 'personal_car_brand_id', 'id');
    }
}

It’s exactly the same code that we used in our PersonalCarBrand. We can now eager load it and check to see the results.

> PersonalCarModel::with('cars')->find(3);

= App\Models\PersonalCarModel {#4735
    id: 3,
    name: "Consequuntur",
    slug: "consequuntur",
    created_at: "2023-01-20 20:37:11",
    updated_at: "2023-01-20 20:37:11",
    cars: Illuminate\Database\Eloquent\Collection {#4738
      all: [
        App\Models\PersonalCar {#4744
          id: 4,
          year: "2018",
          personal_car_brand_id: 3,
          personal_car_model_id: 1,
          is_manual: 0,
          exterior_color: "mollitia",
          purchase_amount: 47028382,
          current_value: 285870699,
          sales_amount: 0,
          date_purchased: "1985-05-25",
          date_sold: null,
          created_at: "2023-01-20 20:37:11",
          updated_at: "2023-01-20 20:37:11",
        },
      ],
    },
  }

>

For image, there’s a little more to it, so we’ll tackle that one in the next article since I want to make sure that you understand the pivot table concept thoroughly. I think we covered enough here. See you next time.

Laravel Series

Continue your Laravel Learning.

Laravel — P36: Seeders and Factories (CMP)

Master Data Generation with Laravel Seeders and Factories

Laravel – P36: CMP – Seeders/Factories

Learn how to efficiently use Laravel seeders and factories for data generation in your projects. Boost productivity and streamline your workflow.

Laravel — P37: Models $fillable, belongsTo and hasMany (CMP)

Mastering Models in Laravel: $fillable, belongsTo, and hasMany

Laravel – P37: CMP – Models $Fillable, Belongsto and  Hasmany

Explore Laravel models with a focus on $fillable, belongsTo, and hasMany. Understand how to efficiently manage relationships and data handling in your projects.

Laravel — P38: Many to Many Relationship (CMP)

Laravel Relationships Unlocked: Using belongsToMany, attach, and detach

Laravel – P38: CMP – Many To Many Relationship belongsToMany, attach, detach

Learn how to manage many-to-many relationships in Laravel using belongsToMany, attach, and detach. Simplify complex data links in your applications.

Leave a Reply