I'm rather new to Eloquent (and ORM's as a whole really). I have done rather a lot of background reading but can't quite get my head around the relationships in Eloquent.
I have a Car model that relates to a Color model, a Make model and a Model model.
I pass my Car::getAll() through to my View as $cars. When I call dd(toArray($cars)) I get the following:
array (size=1)
0 =>
array (size=12)
'id' => string '1' (length=1)
'registration' => string '123' (length=3)
'make' =>
array (size=5)
'id' => string '1' (length=1)
'title' => string 'Ford' (length=4)
'slug' => string 'ford' (length=4)
'created_at' => string '2014-06-26 21:30:23' (length=19)
'updated_at' => string '2014-06-26 21:30:23' (length=19)
'model' =>
array (size=5)
'id' => string '1' (length=1)
'title' => string 'Mustang' (length=7)
'slug' => string 'mustang' (length=7)
'created_at' => string '2014-06-26 21:30:41' (length=19)
'updated_at' => string '2014-06-26 21:30:41' (length=19)
'color' =>
array (size=5)
'id' => string '1' (length=1)
'title' => string 'Red' (length=3)
'slug' => string 'red' (length=3)
'created_at' => string '2014-06-26 21:30:03' (length=19)
'updated_at' => string '2014-06-26 21:30:03' (length=19)
'year' => string '1991' (length=4)
'is_classic' => string '1' (length=1)
'price' => string '999.00' (length=6)
'sold' => string '0' (length=1)
'active' => string '1' (length=1)
'created_at' => string '2014-06-26 22:17:27' (length=19)
'updated_at' => string '2014-06-26 22:17:27' (length=19)`
Which appears to be right to me, however when I have:
foreach ($cars as $car) {
echo $car->color-title;
}
I get a "Trying to get property of non-object" error.
My Models are as follows:
Car.php
class Car extends \Eloquent {
protected $fillable = ['color_id'];
public function color() {
return $this->belongsTo('Color', 'id');
}
public function model() {
return $this->belongsTo('Model', 'id');
}
public function make() {
return $this->belongsTo('Make', 'id');
}
public static function getAll() {
return Car::with('color', 'make', 'model')->where('active', 1)->get();
}
}
Color.php
class Color extends \Eloquent {
protected $fillable = ['title', 'slug'];
public function cars() {
return $this->hasMany('Car', 'color');
}
}
Make.php
class Make extends \Eloquent {
protected $fillable = [];
public function cars() {
return $this->hasMany('Car', 'make');
}
}
Model.php
class Model extends \Eloquent {
protected $fillable = [];
public function cars() {
return $this->hasMany('Car', 'model');
}
}
Any help would be very much appreciated. Thank you
EDIT:
Sorry I should have included my schema up methods:
CreateMakesTable
public function up()
{
Schema::create('makes', function(Blueprint $table)
{
$table->increments('id');
$table->string('title');
$table->string('slug');
$table->timestamps();
});
}
CreateModelsTable
public function up()
{
Schema::create('models', function(Blueprint $table)
{
$table->increments('id');
$table->integer('make')->unsigned();
$table->string('title');
$table->string('slug');
$table->timestamps();
});
Schema::table('models', function(Blueprint $table)
{
$table->foreign('make')->references('id')->on('makes')->onDelete('cascade');
});
}
CreateColorsTable
public function up()
{
Schema::create('colors', function(Blueprint $table)
{
$table->increments('id');
$table->string('title');
$table->string('slug');
$table->timestamps();
});
}
CreateCarsTable
public function up()
{
Schema::create('cars', function(Blueprint $table)
{
$table->increments('id');
$table->string('registration');
$table->integer('make')->unsigned();
$table->integer('model')->unsigned();
$table->integer('year');
$table->integer('color')->unsigned();
$table->boolean('is_classic');
$table->float('price');
$table->boolean('sold');
$table->boolean('active');
$table->timestamps();
});
Schema::table('cars', function(Blueprint $table)
{
$table->foreign('make')->references('id')->on('makes')->onDelete('cascade');
$table->foreign('model')->references('id')->on('models')->onDelete('cascade');
$table->foreign('color')->references('id')->on('colors')->onDelete('cascade');
});
}
Like Jason pointed out the error is caused by null returned from one of those relationships. But the problem with your setup is, that the relationship definitions are wrong.
So first make them right:
// it goes like this:
// belongsTo('Model', 'foreign_key_on_this_model', 'primary_key_on_related')
public function color() {
return $this->belongsTo('Color', 'color'); // primary key is id so no need to specify
}
hasMany relations on the other models are OK.
Then in that loop:
foreach ($cars as $car)
{
if (count($car->color))
{
// do something with color
}
}
For the reason I'm using count read this: Laravel check if related model exists
You can also return only cars that have related color, make and whatever you need like below:
$cars = Car::has('color')->has('make')...
Related
how can i store data to database in tables
i have two tables in my migration
I want to save the "firstName" to "usersAppointments" table but, it always trying to save the data to "appointments" table
"I'm Beginner"
MIGRATION
public function up()
{
Schema::create('appointments', function (Blueprint $table) {
$table->id();
$table->string('');
$table->string('');
$table->date('');
$table->timestamps();
});
Schema::create('usersAppointments', function (Blueprint $table) {
$table->id();
$table->string('firstName');
$table->timestamps();
});
}
CONTROLLER
public function store(Request $request){
$data = $request->validate([
'' => 'required',
'' => 'required',
'' => 'required'
]);
Appointment::create($data);
return redirect('/');
}
public function usersAppointment(Request $request){
$data = $request->validate([
'firstName' => 'required'
]);
Appointment::create($data);
return redirect('/');
MODEL
protected $fillable = [
'', '', '',
'firstName'
];
That's because of trying to insert the data into 'Appointment'.
you must write the code as below :
public function usersAppointment(Request $request){
$data = $request->validate([
'firstName' => 'required'
]);
UsersAppointment::create($data);
return redirect('/');
}
I have 2 table that separate users' login info (email, username, password) and general info (full_name, address, etc).
I set users as parent table with UUID as the PrimaryKey, and profiles as child table with UUID as the PrimaryKey & ForeignKey (I don't know if this is possible, but migration runs without any error).
users_migration
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('username');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
user_model
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Traits\UUID;
class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, Notifiable, UUID;
protected $table = "users";
public $primaryKey = "id";
public $incrementing = false;
protected $fillable =
[
'id',
'username',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
public function profile()
{
return $this->hasOne(Profile::class);
}
}
profiles_migration
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->uuid('user_id')->primary();
$table->foreign('user_id')->references('id')->on('users')->onDelete('restrict')->onUpdate('cascade');
$table->string('full_name')->unique();
$table->longText('address')->nullable();
$table->timestamps();
});
}
profile_model
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
use HasFactory;
protected $table = "profiles";
public $primaryKey = "user_id";
public $incrementing = false;
protected $fillable =
[
'user_id',
'full_name',
'address',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
register_controller
public function Register(Request $request)
{
$newUserData = $request->validate(
[
'full_name' => ['required', 'string', 'min:5', 'max:30'],
'email' => ['required', 'unique:users', 'regex:(gmail)'],
'password' => ['required', 'min:10'],
],
[
'email.regex' => "Your email must use Gmail as its domain.",
'term.accepted' => "You must agree to Larasight's Terms and Conditions."
]
);
User::create([
'username' => Str::lower($newUserData['full_name']),
'email' => $newUserData['email'],
'password' => bcrypt($newUserData['password']),
]);
Profile::create([
'full_name' => $newUserData['full_name'],
]);
}
Error upon registrating : SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value
Question : How to fill the user_id and full_name on profiles_table as the user registrates?
I think you are doing this in an odd way. I would recommend just have an 'id' on your profiles table and have it as the primary. Rather than 'user_id' which is a foreign key be the primary. Anyways, besides that, when you create the Profile object you have not set the primary key. So you would either have to make it nullable in your profiles migration or new up a profile instance without persisting. Lastly, I guess you could just store the User::create() to a variable and pass the id as 'user_id' in the Profile::create function.
$user = User::create([
'username' => Str::lower($newUserData['full_name']),
'email' => $newUserData['email'],
'password' => bcrypt($newUserData['password']),
]);
Profile::create([
'user_id' => $user->id,
'full_name' => $newUserData['full_name'],
]);
I got this error when try to seed database.
Laravel 7.
BlogPost Model
class BlogPost extends Model
{
protected $fillable = [
'title',
'slug',
'user_id',
'category_id',
'excerpt',
'content_raw',
'content_html',
'is_published',
'published_at',
'updated_at',
'created_at',
];
public function category()
{
return $this->belongsTo(BlogCategory::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
User model
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
User migration
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
BlogPost migration
Schema::create('blog_posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('category_id');
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->string('slug')->unique();
$table->text('excerpt')->nullable();
$table->text('content_raw');
$table->text('content_html');
$table->boolean('is_published')->default(false)->index();
$table->timestamp('published_at')->nullable();
$table->foreign('category_id')->references('id')->on('blog_categories');
$table->timestamps();
});
User seeder
class UserTableSeeder extends Seeder
{
public function run()
{
$users = [
[
'name' => 'Author',
'email' => 'seriiburduja#mail.ru',
'password' => bcrypt('some1234')
],
[
'name' => 'Admin',
'email' => 'seriiburduja#gmail.com',
'password' => bcrypt('some1234')
]
];
DB::table('users')->insert($users);
}
}
BlogPost Factory
$factory->define(BlogPost::class, function (Faker $faker) {
$title = $faker->sentence(rand(3, 8), true);
$text = $faker->realText(rand(1000, 4000));
$isPublished = rand(1, 5) > 1;
$createdAt = $faker->dateTimeBetween('-6 months', '-1 day');
return [
'category_id' => rand(1, 10),
'user_id' => 1,
'title' => $title,
'slug' => Str::slug($title),
'excerpt' => $faker->text(rand(100, 400)),
'content_raw' => $text,
'content_html' => $text,
'is_published' => $isPublished,
'published_at' => $isPublished ? $faker->dateTimeBetween('-6 months', '-1day') : null,
'created_at' => $createdAt,
'updated_at' => $createdAt
];
});
DatabaseSeeder
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
$this->call(UserTableSeeder::class);
$this->call(BlogCategorySeeder::class);
factory(BlogPost::class, 1)->create();
}
}
When i run php artisan migrate:fresh --seed i got this error.
Tables users and blog_categories seeds successfully, but error appear for blog_categories.
I don't understand why.
Field user_id exists in $fillable in BlogPost Model.
If i change migration for blog_posts and add a nullable for user_id, than seed work, but user_id is null. But i don't need that.
Thansk in advance.
In Blog Post Model
Change user relationship to
public function owner()
{
return $this->belongsTo(User::class);
}
In User Model
Add this relationship
public function blogposts()
{
return $this->hasMany(BlogPost::class);
}
In Database seeder don't use UserSeeder Directly create user in DatabaseSeeder
public function run()
{
$user = User::create([
'name' => "Your name",
'email' => "youremail#gmail.com",
'password' => Hash::make('YourPassword')
]);
$this->call(BlogCategorySeeder::class);
$user->blogposts()->saveMany(BlogPost::factory(1));
}
In BlogPost Factory remove user_id
return [
'category_id' => rand(1, 10),
'title' => $title,
'slug' => Str::slug($title),
'excerpt' => $faker->text(rand(100, 400)),
'content_raw' => $text,
'content_html' => $text,
'is_published' => $isPublished,
'published_at' => $isPublished ? $faker->dateTimeBetween('-6 months', '-1day') : null,
'created_at' => $createdAt,
'updated_at' => $createdAt
];
fillable is not required when you are using Seeder to insert data.
If you want to insert each and every column in database then you can use guarded property which is opposite of fillable.
I am creating a seeder in laravel, but I have to write 1 column json data, but I could not do it, how can I create a seeder column.
Seeder In seeder there will be "bank name" and "iban" in iban column
Migration
public function up()
{
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->string('account_type');
$table->string('name');
$table->json('ibans')->nullable();
$table->timestamps();
});
}
Seeder
public function run()
{
DB::table('settings')->insert([
'name' => 'Şirket Ünvanı',
'account_type' => 'settings',
'ibans' => json_decode('"a":1,"b":2',true),
'created_at' => now(),
'updated_at' => now(),
]);
}
It's
public function run()
{
DB::table('settings')->insert([
'name' => 'Şirket Ünvanı',
'account_type' => 'settings',
'ibans' => json_encode(['bankname' => 1, 'iban' => '2', '0' => true]),
'created_at' => now(),
'updated_at' => now(),
]);
}
You can add this json field to your Model (Settings) as casting field (protected $cast). It will be automatically handles json_encode, -decode to an array.
protected $casts = [
'ibans' => 'array'
];
$settings->ibans = $array;
$array = $settings->ibans;
https://laravel.com/docs/8.x/eloquent-mutators#array-and-json-casting
I have a ‘recurring_payments’ table
Schema::create('recurring_payments', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('user_id');
$table->string('card_id');
$table->string('day');
$table->integer('amount');
$table->timestamps();
});
I have a ‘card’ table
Schema::create('cards', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->string('token');
$table->string('last4');
$table->timestamps();
$table->softDeletes();
});
I have a ‘belongs to’ relationship between a ‘recurring_payment’ and a ‘card’
class RecurringPayment extends Model
{
protected $guarded = [];
public function card()
{
return $this->belongsTo(Card::class, 'token');
}
}
When I try to store the recurring payment
class Card extends Model
{
use SoftDeletes;
protected $fillable = [
'user_id',
'token',
'last4',
];
protected $hidden = [
'user_id',
'token',
'created_at',
'updated_at',
];
...
public function storeRecurringPayment($payment, $user)
{
$attributes = [
'user_id' => $user,
'card_id' => $payment->source['id'],
'day' => Carbon::now()->day()->addMonths(1)->format('d'),
'amount' => $payment->amount
];
// dd($attributes);
return $this->recurringPayment()->create($attributes);
}
public function recurringPayment()
{
return $this->hasOne(RecurringPayment::class);
}
...
}
it will not populate the ‘card_id’, it complains that the ‘card_id’ cannot be null, which is what I want, so far enough, but the ‘card_id’ is populated, this is the $attributes array died and dumped just before it's passed to the 'return $this->recurringPayment()->create($attributes);'
array:4 [▼
"user_id" => 3
"card_id" => "src_4zcdnrruvgxetpkgxocg6hhk5m"
"day" => "30"
"amount" => 10
]
I’m specifying in the ‘recurring_payment’ that I want to use ‘token’ as the foreign key not the default ‘card_id’ but I still get this error
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'card_id' cannot be null (SQL: insert into 'recurring_payments' ('user_id', 'card_id', 'day', 'amount', 'updated_at', 'created_at') values (3, ?, 30, 10, 2019-07-11 13:53:08, 2019-07-11 13:53:08))
Can anyone see the mistake I've made?