How to manage relationships(fk) of models? - php

I know how to create a model - php artisan make:model nameofmodel, i know that in the migration of the model i have to declare what columns will the table have:
class CreateNameofmodelTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('nameofmodels', function(Blueprint $table)
{
$table->increments('id');
$table->string('name')->unique();
$table->string('description');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('nameofmodels');
}
}
But if i want to relate two models using a FK, what are the configurations i have to set in the migration file or in the model file?

Suppose you have another model User with table name users. you can have relationship defined between addresses and users as following,
public function up()
{
Schema::create('addresses', function(Blueprint $table)
{
$table->increments('id');
$table->string('add1');
$table->string('add2');
$table->string('city');
$table->string('contact_no');
$table->enum('is_primary',['yes','no'])->default('no');
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
In User model class,
class User extends Model {
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'users';
public function addresses()
{
return $this->hasMany('App\Address');
}
}
in Address class
class Address extends Model {
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'addresses';
public function user()
{
return $this->belongsTo('App\User');
}
}
More detailed information about creating table can be found on Official Schema Docs on Laravel. And More information about Relation ship here
UPDATE
According to this question, you will have to create referenced table first before referencing table. In our case, users must exists before you run migration to create addresses table.
Simple Use-case
suppose, in controller you want to find out how many addresses particular users have, you can do like following,
$addressCount = User::find($userId)->addresses->count();
Please note User::find($userId)->addresses returns collection of related models while User::find($userId)->addresses() returns object of type HasMany
you can also traverse collection as below,
foreach(User::find($userId)->addresses as $address)
{
echo '<pre>';
print_r($address);
}
On the other side, you can get user associated with particular address as below,
$user = Address:find($addressId)->user; //returns object of type `User`
echo $user->first_name;
following will not work
$user = Address:find($addressId)->user(); //returns object of type `belongsTo`
echo $user->first_name;
Note, above Address:find($addressId)->user will not return collection but a single object of class User. This is because a belongsTo relationship.
I hope I made things clear for you. But nothing as good as official docs.
Laracast videos are even better to get on with Laravel.

you cal use like this...
class CreateNameofmodelTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('nameofmodels', function(Blueprint $table)
{
$table->increments('id');
$table->string('name')->unique();
$table->string('description');
$table->timestamps();
});
Schema::create('your table name', function(Blueprint $table) {
$table->foreign('user_id')->references('id')->on('nameofmodels');
}
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('nameofmodels');
}
}
check it..may be it is working perfectly....

Related

How to seed tables orderly to avoid "Integrity constraint violation" in Laravel 9.x

I have a bookstore project, and have 2 table: publishers and books.
These is my two migrate file for books and publishers.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBooksTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->integer('available_quantity');
$table->string('isbn');
$table->string('language');
$table->integer('total_pages');
$table->float('price');
$table->string('book_image');
$table->string('description')->nullable();
$table->date('published_date');
$table->unsignedBigInteger('publisher_id');
$table->foreign('publisher_id')->references('id')->on('publishers');
$table->unique('isbn');
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('books');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('publishers', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->string('address');
$table->string('phone');
$table->string('description')->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('publishers');
}
};
As you see, books has a foreign key publisher_id, which has reference to publishers on id, so when I run php artisan db:seed,both tables will be seeded at the same time, but as we know, the publishers table should be seeded before books table be. So are there any way to seed tables orderly (not at the same time) ?
Well you can actually create the related publisher when creating the books in a seeder.
// You can use the "for()" if you define it in a child table and "has()" when define in a parent table.
Book::factory(10)->for(Publisher::factory()->create())->create(); // will create 10 books but only belongs to one publisher
Publisher::factory(10)->has(Book::factory()->count(10))->create(); // will create 10 books in each publisher. it means that you will have a 100 books.
You can also directly define it in the Database\Factories\BookFactory::class
public function definition()
{
return [
'publisher_id' => Publisher::factory(), //add this to the BookFactory::class
];
}
Book::factory(10)->create(); // will create 10 book but also create 10 publisher
Eloquent Factories: Factory Relationship
Personally I prefer to make Publisher seeder first, then in set the value of Book's publisher_id in the factory to be random from the existing Publisher.
Create 100 Publisher, or any amount you wish to set
Publisher::factory()->count(100)->create();
Ramdomly set publisher_id from the existing records
return [
// ....
'publisher_id' => Publisher::inRandomOrder()->first()->id
];
You have a file named DatabaseSeeder. You can choose the order of the seeder there:
public function run()
{
$this->call([
PublisherSeeder::class,
BookSeeder::class,
]);
}
If you put PublisherSeeder first, the seeder will be called before BookSeeder and you won't have the constraint integretity error.

Laravel relationships seems to not working

I have an Item and AdvertItem objects in Laravel. I want to create a 1 to 1 relationship between an item and advert item
The item class looks like this
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Item extends Model
{
//
public function Category(){
return $this->belongsTo(Category::class);
}
public function Currency(){
return $this->hasOne(Currency::class);
}
public function AdvertItem(){
return $this->hasOne(AdvertItems::class);
}
}
and the AdvertItem class looks like this
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AdvertItems extends Model
{
protected $guarded = [];
//
public function items(){
return $this->belongsTo(Item::class);
}
}
but when I call advertItem I only see item_id = 1 instead of item object.
Item table is created like this
class CreateItemsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('description');
$table->unsignedBigInteger('currency_lookup_id');
$table->unsignedBigInteger('category_id')->index();
$table->unsignedBigInteger('price');
$table->string("image_path");
$table->string('sale_ind');
$table->Date('eff_from');
$table->Date('eff_to');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('item');
}
}
And advert table is created like this
class CreateAdvertItemsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('advert_items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('item_id');
$table->unsignedBigInteger('customer_id');
$table->Date('eff_from');
$table->Date('eff_to');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('advert_items');
}
}
please assist.
The following rules will assist you.
Always start relationship names with lower case. Save capitals for Classes not methods.
Models should be Singular
Pay attention to the plurality of names. Things that there should only be one of should be singular. So, in your 1:1 relationships, both relationship names should be singular.
AdvertItem class
public function item(){
return $this->belongsTo(Item::class);
}
then, if you have Item and want AdvertItem, you should load it
$item->load('advertitem');
or the other way around
$advertItem->load('item');

Getting null as a result

I have created two tables, First is Posts table and seconds is Comments table.
I'm using sqlite database.
I'm getting null as result for comment function using hasMany method in Post Model.
Posts table migration file:
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('Posts', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('Posts');
}
}
Comments table migration file:
class CreateCommentsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('Comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned()->index();
$table->text('body');
$table->timestamps();
});
Schema::table('Comments', function (Blueprint $table) {
$table->foreign('post_id')->refrences('id')->on('Posts');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('Comments');
}
}
Post Model:
class Post extends Model
{
//
public function comment(){
return $this->hasMany(Comment::class);
//I have also tried: return $this->hasMany(Comment::class,'post_id','id');
}
}
The data has been entered in both the tables and I'm getting results when I do
$post=new App\Post;
$post->get();
But when I try to get all the comments for a post using
$post->comment;
I'm getting
=>Illuminate\Database\Eloquent\Collection {#633
all:[],
}
Why am I getting null as a result?
You're not. You're getting an empty Collection object. This means there are no results for your post found.
You can do $post->comment->count() === 0 and get true.
Your relationship appears wrong. You said you tried something else; that one looks more correct, going by the documentation.
https://laravel.com/docs/5.0/eloquent
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
With this, if you are still getting an empty set as a return, I would encourage you to install the Laravel debug bar and check out the raw SQL being ran. Your data is either incorrect or there is some problem that isn't being explained in your post.
Edit: You can also try setting up a belongsTo relationship on the Comment for good measure.

Laravel belongsTo throwing undefined function App/BelongsTo() exception

I have a comments table and user table with the relationship: user->hasMany('comments') and comment->belongsTo('user'). For some reason with which eludes me, I keep getting this
FatalErrorException in Comment.php line 22: Call to undefined function App\belongsTo()
My other models have no issue whatsoever with the HasMany relations, however, the comments model has a problem with every single thing i try (even if i use HasMany just to see if it will have a different error).
Here is the Comment model.
use Illuminate\Database\Eloquent\Model;
class Comment extends Model {
protected $table = 'comments';
protected $fillable = [
'anime_id',
'user_id',
'user_comment'
];
public function postedOnAnime($query)
{
$query->where('anime_id', '=', $this.id);
}
public function user()
{
return $this.belongsTo('App\User', 'user_id', 'id');
}
public function anime()
{
return $this.belongsTo('App\Anime');
}
}
Here is the Users table:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->integer('role');
$table->string('name')->unique();
$table->string('email')->unique();
$table->string('password', 60);
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('users');
}
}
Here is the Comments table
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCommentsTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('comments', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('anime_id')->unsigned();
$table->text('user_comment');
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->foreign('anime_id')
->references('id')
->on('animes')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('comments');
}
}
Finally, when I call $comment->user() it fails with the error. Does anyone know where this error comes from?
Thanks.
Well, this error occurred because I had '.' in place of '->'. I couldn't figure out why it was always throwing the exact same error regardless if I did $this.belongsTo('App\User'); or $this.hasMany('App\User'); or even $this.thecakeisalie('App\User'); until I sat staring at the text between my many models yet again. Then, lo and behold, its another dumb, tiny and really hard to locate mistake of mine(as it usually is).
return $this.belongsTo('App\User', 'user_id', 'id');
to
return $this->belongsTo('App\User');
I think this is a common mistake coming from other languages where . is used to access properties. In PHP, it should be ->.
$this.belongsTo('...')
should be rewritten as
$this->belongsTo('...')

Can't retrieve a one to many relationship in Laravel 4

I'm trying to get to a ProfileType through my User model, i.e. $user->profile->profiletype; I'm not able to retrieve an object, however. Basically, User hasOne Profile, and Profile belongsTo User and ProfileType. ProfileType hasMany Profile.
My table names are users, profiles and profile_types.
models/User.php
use Cartalyst\Sentry\Users\Eloquent\User as SentryUserModel;
class User extends SentryUserModel {
/**
* Indicates if the model should soft delete.
*
* #var bool
*/
protected $softDelete = true;
public function profile()
{
return $this->hasOne('Profile');
}
}
models/Profile.php
class Profile extends Eloquent {
protected $fillable = array('username', 'slug', 'completed');
/**
* #return
*/
public function user()
{
return $this->belongsTo('User');
}
public function profiletype()
{
return $this->belongsTo('ProfileType');
}
}
models/ProfileType.php
class ProfileType extends Eloquent {
/**
* #return
*/
public function profiles()
{
return $this->hasMany('Profile');
}
}
Profile and ProfileType migrations
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
// profile_types table
class CreateProfileTypesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('profile_types', function(Blueprint $table) {
$table->integer('id', true);
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('profile_types');
}
}
// profiles table
class CreateProfilesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('profiles', function(Blueprint $table) {
$table->integer('id', true);
$table->string('username');
$table->string('slug');
$table->boolean('completed');
$table->integer('user_id');
$table->integer('profile_type_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('profiles');
}
}
I think you may have found a bug with the way Laravel handles foreign keys. It should know that the foreign key of profile_types is profile_type_id but it's actually looking for profiletype_id.
So you can either change that column in your table so you don't have to worry about sending extra parameters each time you need another relationship on that table, or in your Profile model, you can make your function like this...
function profiletype
{
return $this->belongsTo('ProfileType', 'profile_type_id');
}
Then you should be able to find a user's profile type with...
$user = User::find(1);
echo $user->profile->profiletype->name;
echo $user->profile->profiletype->slug;

Categories