I have a table users in a one-many relationship with a table called videos. I want to seed the two table without loosing data integrity. This is what I did below:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'username' => $faker->unique()->userName
];
});
$factory->define(App\Video::class, function(Faker\Generator $faker){
$user = factory(App\User::class)->create();
return [
'title' => $faker->city,
'link' => $faker->domainName,
'user_id' => $user->id,
'description' => $faker->sentence(40)
];
});
So now all I have to do is create a VideoTableSeeder and run the amount I want. I feel like the way I am handling it is not good enough so I would like to know what better way I can do it. Especially, I want the videos to be more than users rather than the same amount, in the one I had done they will all be the same amount.
Checkout the documentation the section Adding Relations to Models
Adding Relations To Models
You may even persist multiple models to the database. In this example,
we'll even attach a relation to the created models. When using the
create method to create multiple models, an Eloquent collection
instance is returned, allowing you to use any of the convenient
functions provided by the collection, such as each:
$users = factory(App\User::class, 3)
->create()
->each(function($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
Related
I currently have a users table and a books table, with a pivot table user_book which has user_id, book_id as well as book_tag (this can be 'H' for happy, 'S' for sad or 'A' for angry)
Against the advice of the backpack team, we are looking to have three multiselect options, which will popoulate with the 3 different types of book tags, i.e. Happy books, Sad books, and Angry books.
I currently have the following definition inside the initFields function:
<?php
namespace App\Http\Controllers\Admin;
class UserCrudController extends UserCrudController
{
// ....
protected function initFields()
{
// crud fields here
$this->crud->addField([
'label' => "Happy books",
'type' => 'select2_multiple',
'name' => 'books_h',
'entity' => 'books',
'model' => "App\Models\Book",
'pivot' => true,
]);
}
}
This however, does not seem to save. Any assistance is greatly appreciated
you need to make sure the the relation books_h is defined correctly in your entity as belongsToMany relationship based on laravel docs https://laravel.com/docs/9.x/eloquent-relationships#many-to-many
like so
public function books_h():
\Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Book::class, 'user_book', 'user_id', 'book_id', 'id', 'id')->withPivot('book_tag');
}
then you need to overwrite both create and update methods to update the request for these fields to transfer it to look like so
[
1 => ['book_tag' => 'H'],
2 => ['book_tag' => 'H'],
]
before calling $response = $this->traitUpdate();
refer to this link for more info https://backpackforlaravel.com/docs/5.x/crud-operation-update#override-the-update-method
but I would recommend to use the correct way backpack team mentions https://backpackforlaravel.com/docs/5.x/crud-fields#save-additional-data-to-pivot-table even if you want to have it as 3 separate fields you can define the subfields as hidden with the value you want
I'm using Laravel 8 and the unique validation rule to ensure that a record remains unique, I'm now trying to extend this so that it's unique per user as well, but when expanding the functionality and using the rule in array form it doesn't seem to validate the user ID and instead gives me a integrity constraint violation.
So I have a table called brands, and this table contains two columns in question: brand and user_id, I need to ensure that when storing a record that the brand is unique against the brand column and that the logged in in user's ID the one making the request, e.g:
Two users can have the same brand, but a single user can't have multiples of the same brand.
$validator = Validator::make($request->all(), [
'brand' => [
'required',
'string',
Rule::unique('brands')->where(function ($query) {
return $query->where('user_id', Auth::id());
})
],
'url' => 'required|string',
'telephone' => 'required|string|min:11|max:11'
]);
I've also tried:
'brand' => 'required|string|unique:brands,brand,user_id,' . Auth::id()
What am I missing?
According to the documentation you have to use the ignore() function:
Rule::unique('users')->ignore($user->id),
on your case:
Rule::unique('brands')->ignore($user->id, 'user_id'),
Sample query:
TableRegistry::getTableLocator()
->get('Parents')
->find()
->contain([
'Children' => function (Query $query) {
return $query->where([
'Children.code = Parent.code'
]);
}
])
Parent and Children tables only have code as common field.
How do I define their association?
How do I contain unassociated entities?
You can customize the foreignKey and bindingKey in association configuration
In ParentsTable.php:
$this->hasMany('Children', [
'bindingKey' => 'code',
'foreignKey' => 'code'
]);
This config will set which fields to look for when associating entities.
Then you could associate entities on your controller like this:
// This query will contain children where Children.code === Parent.code
TableRegistry::getTableLocator()
->get('Parents')
->find()
->contain('Children');
You can define the relationship between those tables througth ParentesTable as #kgbph explained.
Although in your exemple I think the better solution is use Cake's TreeBehavior. Here's the link to the documentation.
My application allows a user to create scenarios by linking together soe_blocks. In turn, soe_blocks refer to a variable number of soe_entries.
To build scenarios, soe_blocks are linked to the scenario and ordered by an offset. The soe_blocks can be used in many different scenarios. soe_entries can relate only to a single soe_block
I think the relationship is defined as:
scenarios belongsToMany soe_blocks through scenarios_soe_blocks
soe_blocks belongsToMany scenarios through scenarios_soe_blocks
scenarios_soe_blocks is where the offset is kept
soe_entries haveOne soe_blocks
Tables:
scenarios: id | name
data: 0, 'scenario_1'
soe_blocks: id | name
data: 0, 'soe_block_1'
1, 'soe_block_2'
scenarios_soe_blocks: id | scenario_id | soe_block_id | offset
data: 1, 0, 1, 1
2, 0, 2, 2
Models:
class ScenariosTable extends Table
{
$this->belongsToMany('SoeBlocks', [
'foreignKey' => 'scenario_id',
'targetForeignKey' => 'soe_block_id',
'through' => 'ScenariosSoeBlocks',
'joinTable' => 'soe_blocks'
]);
}
class SoeBlocksTable extends Table
{
$this->belongsToMany('Scenarios', [
'foreignKey' => 'soe_block_id',
'targetForeignKey' => 'scenario_id',
'joinTable' => 'scenarios_soe_blocks',
'through' => 'ScenariosSoeBlocks'
]);
}
class ScenariosSoeBlocksTable extends Table
$this->belongsTo('SoeBlocks', [
'foreignKey' => 'soe_block_id',
'joinType' => 'INNER'
]);
}
Controllers:
public function view($id = null)
{
$scenario = $this->Scenarios->get($id, [
'contain' => ['SoeBlocks', 'RunStatus', 'ScenarioLog']
]);
$this->set('scenario', $scenario);
}
As far as I can make out from CakePHP Doc, this is all I need. But I couldn't get the ScenarioController->view() method to return the offsets from the scenarios_soe_blocks table associated with the soe_blocks.
I tried to add ScenariosSoeBlocks into the 'contain' clause in the ScenarioController, but got the error: Scenarios is not associated with ScenariosSoeBlocks. I found an SO article that suggested I add the following to the ScenarioTable:
$this->hasMany('ScenariosSoeBlocks', [
'foreignKey' => 'scenario_id'
]);
This seems to have worked, and now I can request ScenariosSoeBlocks in my controller like this:
$scenario = $this->Scenarios->get($id, [
'contain' => ['SoeBlocks', 'ScenariosSoeBlocks', 'RunStatus', 'ScenarioLog']
]);
Which at least gets the data into the view template, but not in the single object I'm hoping for. Eventually, I want to be able to CRUD the soe_blocks along with their associated soe_entries, in an object that looks like this:
offset | soe_block_id | soe_entry_id |
I have many other questions, like how to save etc., but I figured I need to get this working first.
So, my questions for now are:
are my associations correct?
how do I retrieve all the associations to view?
are my associations correct?
The first two are, but then it should be:
soe_blocks hasOne soe_entries
soe_entries belongsTo soe_blocks
how do I retrieve all the associations to view?
By containing them, just like you did in your first example. This question seems to originate from the question how to access the join table data, which is very simple, the join table data is being set on the target table entity (Scenario or SoeBlock, depending on from which side/table you issue the query), in a property named _joinData:
$joinTableEntity = $scenario->soe_blocks[0]->_joinData;
$offset = $joinTableEntity->offset;
You can easily gather information about the data structure by dumping your entity contents:
debug($scenario);
See also
Cookbook > Database Access & ORM > Associations - Linking Tables Together
Cookbook > Database Access & ORM > Saving Data > Saving Additional Data to the Join Table
I am working on application that is made up of Leads, each Lead -> hasMany -> Fields. My application will accept an infinite amount of whitelisted fields. Some Leads will have a lot of Fields, others will have maybe around 5. Due to this, I've opted for the Field table to be vertical, rather than fit in all the fields I accept horizontally, and then run into MySQL errors down the line (table too wide, etc.)
Here's my structure:
Lead Table
id
...
Field Table:
id
lead_id
field_name
field_value
I have created a model factory for my Lead model, that automatically creates 5 random fields using Faker.
I have an array of texts, numbers, dates (etc) fields which my application accepts.
Field Factory:
...
$texts = config('fields.whitelist.text');
foreach ($texts as $text) {
$fields[$text] = $faker->sentence();
}
...
$randomField = array_random(array_keys($fields));
return [
'field_name' => $randomField,
'field_value' => $fields[$randomField],
];
I've been doing this:
$lead = factory(Lead::class)->create()
->each(function ($l) {
$l->fields()->save(factory(Field::class, 5)->make());
});
However, I now have a minimum array of Fields which each Lead must have. I have these minimum fields in another config.
Is it possible to automatically create the x minimum Fields on the vertical table, using a factory?
E.g.
Minimum Fields
first_name
date_of_birth
How can I write a factory to automatically create the following structure:
[
'field_name' => 'first_name',
'field_value' => '<random name>',
],
[
'field_name' => 'date_of_birth',
'field_value' => '<random date>',
],
Edit: and if possible, not insert duplicate field_name values. Not like it's 100% deal breaker, but I want to make sure I 100% know what data I am working with, so checking x number of duplicates I imagine would be a nightmare
If you want each Lead to have those minimum fields, then add those fields to your each() closure. Like this:
$lead = factory(Lead::class)->create()->each(function ($lead) {
$lead->fields()->createMany([
'field_name' => 'first_name',
'field_value' => $faker->firstName,
],
[
'field_name' => 'date_of_birth',
'field_value' => $faker->dateTime(),
]);
$lead->fields()->save(factory(Field::class, 3)->make());
});
I changed the Field factory to 3 because there are 2 fields from the "minimum fields" that are inserted for every Lead.