I want a database with two tables Users and Companies and the users table has a foreign key with the company id. So 1 company can have multiple users.
I created the models for this in laravel and also created a factory for each of the tables.
In my seeders I want to create multiple data lines at once and found this solution:
factory(App\Company::class, 10)->create();
This works fine for the company table. But I need to extend this for the users table so it searches for available company Ids.
Any ideas how I can do this?
If I can't search for available Ids, I would also be happy to extend it with the "company_id" field and give it random value from 1-10 (beacause I know that these are the Ids for now).
So basically I want to extend this to use the fields from the factory, but extend it with another field "company_id":
factory(App\Users::class, 10)->create();
Here is the User factory code:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'postcode' => $faker->postcode,
'city' => $faker->city
];
});
When dealing with such relationships, defining model factories can be a pain. You have a few options though.
Random blind IDs
If you're sure that a common range of IDs is available, you can just generate a random ID to make the relationship work:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => rand(1, 10),
];
});
Inline factories
This was the most widely used approach as promoted by Laracast's screencasts:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => factory(App\Company::class)->create()->id,
];
});
Or you can go the other way around and create one or more users in the company factory. This way, you only need to run factory() on one of your entities.
Random ID from database
As of Laravel's 5.2 release, you have access to an Eloquent method called inRandomOrder:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => Company::inRandomOrder()->first()->id,
];
});
Alternatives for Laravel releases before 5.2:
// Laravel 4.2.7 - 5.1:
User::orderByRaw("RAND()")->get();
// Laravel 4.0 - 4.2.6:
User::orderBy(DB::raw('RAND()'))->get();
// Laravel 3:
User::order_by(DB::raw('RAND()'))->get();
// Source: http://stackoverflow.com/a/13931676/65732
It looks like that, you want to create Companies and Users and also you want to attach users with companies at the same time. If this is what you are trying to achieve then you can do it easily using something like this:
factory(App\Company::class, 10)->create()->each(function($factory) {
$factory->users()->save(factory(App\User::class)->make());
});
In this case, you have to define the users relationship method in your Company model using the right foreign key.
This will create 10 companies and return a collection so on that collection using each method a single user will be created and attached with that company. Also, check the documentation.
If you're using Laravel 5.2 or above you can use this:
'company_id' => Company::inRandomOrder()->first()->id
Related
Is it possible to specify the Schema for a Laravel Pivot table when attaching?
We have multiple Schemas, specific schemas for each application and the public being tables that are shared between them.
Our Laravel app has a schema that it is defaulted too in the .env file. Reaching outside the default schema is relatively painless, just needing to explicitly set the table name.
protected $table = "public.table_name_here";
The issue is when we have a pivot table in the public schema, there doesn't seem to be a way to tell Laravel to look at the public schema for it.
Ex.
//users model
public function roles() {
return $this->belongsToMany('App\Roles', 'user_role', 'userId', 'roleId')->withTimestamps();
}
$user->roles()->attach($roleId);
will always look in the laravel schema for the user_role
When I add the user_role to the laravel schema, everything works great.
I've tried to specific to use the user_role table in public by doing the following
//users model
public function roles() {
return $this->belongsToMany('App\Roles', 'public.user_role', 'userId', 'roleId')->withTimestamps();
}
Unfortunately I haven't been able to find any resources on specifying the schema for a pivot table, so any advice on if this is possible and, if so, how would be great.
Thanks in advance.
In config/database.php there is the key connections.pgsql.schema
When laravel connects to PostgreSQL uses this key to send this command to the server:
"set search_path to {$schema}"
You can set this config key to a comma separated list of schemas that will be searched in order to find a table when you don't specify a schema for the table name.
In your case you can set this key in config/database.php to something like this:
return [
//
'connections' => [
//
'pgsql' => [
//
'schema' => 'laravel,public',
With that in place your SQL queries should be able to find the public.user_role table even without specifing the schema, i.e:
public function roles() {
return $this->belongsToMany('App\Roles', 'user_role', 'userId', 'roleId')->withTimestamps();
}
Be aware that if you have a table laravel.user_role it will be found first because in the search path, as configured above, the laravel schema comes first, in this case just invert the order, like this:
'schema' => 'public,laravel',
According to Laravel's documentation on defining relationships within model factories:
You may also attach relationships to models using Closure attributes in your factory definitions. For example, if you would like to create a new User instance when creating a Post, you may do the following:
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
}
];
});
The issue I'm running into is the reference to create() within the relationship definition. It seems to me that this doesn't belong here.
It works great if I am wanting to persist my relationships to the database:
factory(App\Post::class)->create();
By running the code directly above this, a new App\Post and a new App\User will be created and both persisted to the database.
But if I just want to new up the model(s) and not persist anything (at all) to the database by running:
factory(App\Post::class)->make();
It does what I want up to a certain point. A new App\Post instance is created but not persisted, however App\Comment is created and is persisted to the database.
It seems to me, that what I really want is something like this:
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
// here I only want to declare the relationship,
// not decide whether I want to create() or make()
// the relationship, something like:
return factory(App\User::class)->createOrMake()->id;
// or perhaps something like:
return factory(App\User::class)->id;
}
];
});
The end result is that I want the related data to respect what I'm trying to do from the top of the call, down. Make everything. Or create everything.
Am I doing something wrong here? Or is this something that doesn't currently exist?
Thanks!
You want the lazy() method for your related models!
At least in Laravel 5.5.
I found your question here because I was having the same issue. I found the answer thanks to Laravel's beautifully written code -- lazy() was defined just above the make() XDebug took me to -- and I've tried it out and it seems to work.
This is what I'm doing:
$factory->define(App\ArtItem::class, function (Faker $faker) {
return [
// 'id' => $faker->randomDigit,
'slug' => $faker->unique->word,
'artist_id' => factory(App\Artist::class)->lazy(),
'image_id' => factory(App\Image::class)->lazy(),
'created_at' => $faker->dateTime,
'updated_at' => $faker->dateTime,
'deleted_at' => $faker->dateTime,
];
});
Lazy() is an interesting creature that returns a closure, so you can't do factory(App\Image::class)->lazy()->id but I'm still seeing it successfully setting the correct image_id and artist_id.
I certainly hope you found the solution long before this, but maybe this'll help someone else!
This is finally solvable now using Factory Callbacks added in Laravel 5.6.12.
$factory->afterMaking(Post::class, static function (Post $post) {
if (!$post->user_id) {
$post->user()->associate(factory(User::class)->make(['id' => 0]));
}
});
$factory->afterCreating(Post::class, static function (Post $post) {
if (!$post->user_id) {
$post->user()->associate(factory(User::class)->create())->save();
}
});
Now, whenever you make a Post, if you don't pass it a user_id, it will make a User for you and set the relationship without saving it. But if you create a Post, it will persist a new User to the database and save its ID to the Post model.
Note that both the afterMaking and afterCreating closures are called when you create a factory model. I am temporarily setting the User id to 0 in case the foreign key posts.user_id was defined not null. It will be updated to the correct value in afterCreating. This is a terrible hack, but it works until we get a proper beforeCreating hook which would allow us to create the related model first.
Recently I have been trying to seed my database using Laravel seeding through Model Factories and Faker.
For simple schemas, it is just a breeze to have it working :). However, I have encountered several problems when working with complex DB schemas which involve foreign keys and table relationships:
One to One
One to Many
Many to Many
...Like the one described in the link:
Laravel 5.1 foreign keys in model factory.
In this topic, the official documentation suggests to run the database seeds like this:
public function run()
{
factory(App\User::class, 50)->create()->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
}
... but there is one problem with this solution: when working with many DB tables and running many seeds (with many relations between them), it is common to create many unnecessary models using this methodology. For instance, if we had run the PostsTableSeeder.php before the one of the above example, all those posts would not have been linked to users, and would never be used in tests and development...
So searching for a way to handle this situation, I have come up to a functional solution that works for me and avoids the unnecessary creation of those 'orphan' models...
And I wanted to share it with everyone, so it is just explained in the answer :).
So here is my solution:
The example deals with:
Users & Profiles (for illustrating One to One relationships)
Users & Posts (for illustrating One to Many relationships)
// ONE TO ONE relationship (with Users already created)
$factory->define(App\Profile::class, function (Faker\Generator $faker) {
return [
'user_id' => $faker->unique()->numberBetween(1, App\User::count()),
// Rest of attributes...
];
});
// ONE TO MANY relationship (with Users already created)
$factory->define(App\Posts::class, function (Faker\Generator $faker) {
$users = App\User::pluck('id')->toArray();
return [
'user_id' => $faker->randomElement($users),
// Rest of attributes...
];
});
Here is a solution to make relationships that is way better than assigning random Users, especially if you need to send extra information to this model.
$factory->define(App\Post::class, function (Faker\Generator $faker) {
$user = factory('App\Models\User')->create(['email' => 'email#test.com',]);
// do your relationships here (...)
return [
'user_id' => $user->id,
'title' => $faker->sentence,
'body' => $faker->paragraph,
];
}
And I saw another example with the use of anonymous function
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'user_id' => function () {
return factory(App\User::class)->create()->id;
},
'title' => $faker->sentence,
'body' => $faker->paragraph,
];
}
Source: https://laracasts.com/series/laravel-from-scratch-2017/episodes/22
This is what I use for FKs on factories
return [
'user_id' => $this->faker->randomElement(User::pluck('id')),
...
];
Note: make sure that your factories run in the right order
after I did some research, I didn't find any proper answer to this question.
I'm starting writing tests for my CakePHP (3.x) app and I was wondering how can we add fixtures and their associations?. In other words, how can we link a fixture to another without writing the Primary and foreign keys directly inside the fixture.
i.e
I have a Users table and a UserProfiles table.
User hasOne UserProfile through user_id column. Here's my user fixture.
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UsersFixture extends TestFixture
{
public $import = ['table' => 'users'];
public $records = [
[
'username' => 'mark#mail.com',
'primary_role' => 1
'is_active' => 1,
]
];
}
And here's my UserProfile fixture
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UserProfilesFixture extends TestFixture
{
public $import = ['table' => 'user_profiles'];
public $records = [
[
'first_name' => 'Mark',
'last_name' => 'Yellowknife',
]
];
}
How do I link both record together? Aka, how can I tell the user profile record to add its user_id to be linked to the user record?
Must be a way to do this with Fixtures without writing the keys manually.
Thanks guys!
You have to define this in the fixtures. The only other way would be to issue update queries at runtime to set the foreign keys, but that's anything but a good idea.
Other than that there isn't really a way to do this, I mean, how would any code know which records needs to be associated with each other without you explicitly defining it?
So, you'll have to at least define the foreign key, and depending on how primary keys are being generated (auto incrementing integers, UUIDs, etc), you might have to define them too.
I'm trying to associate related models during database seeding in Laravel 4. According to the documentation here, I can do it like this:
$user->roles()->attach(1);
So, in my database seed I'm running:
$package = Package::create([
'name' => $faker->word,
'summary' => $faker->sentence,
'base_price' => $faker->randomFloat(2, 200, 10000)
]);
// Attach 1-5 randomly selected items to this package
foreach(range(1, 5) as $index)
{
$randomItem = Item::orderBy(DB::raw('RAND()'))->first();
$package->items()->attach($randomItem->id);
}
The packages items have already been seeded at this point, and they seed without problems. The above code gives this from Artisan though:
[BadMethodCallException]
Call to undefined method Illuminate\Database\Query\Builder::attach()
Someone here seems to think that the attach() method doesn't actually exist and the docs are wrong, but I find that hard to believe.
TL;DR What is the correct way to create many-to-many relationships in Eloquent?
The function items() in your Package model has to return a BelongsToMany relationship in order to use attach().
public function items() {
return $this->belongsToMany('Item');
}