Laravel 5 - Use variables of model in model factories - php

Okay, let's say that I have a Post model with the attributes name, slug and content. I'd like to generate models with my ModelFactory, but want to set a specific name, which I do by overwriting the value:
factory(App\Post::class)->create(["name" => "Something here"]);
But now I want the slug to auto-generate by using the (new) name and without passing it as argument. Like
"slug" => str_slug($name);
Is this possible or do I need to write the slug manually?
When using the factory below with ->create(['name' => 'anything']); the slug is not created.
My current factory
$factory->define(App\Post::class, function (Faker\Generator $faker) {
static $name;
return [
'name' => $faker->name,
'slug' => $name ?: str_slug($name),
'content' => $faker->sentences(),
];
});

This should do the trick. You can pass a name in manually or let Faker handle it.
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'slug' => function (array $post) {
return str_slug($post['name']);
},
'content' => $faker->sentences(),
];
});

Have you tried
$name ="Something here";
factory(App\Post::class)->create(["name" => $name, "slug" => str_slug($name)]);

Related

Save multi-level array using Laravel Model

I have problem to save this data using Laravel-7 model
this is my data
$supplier = [
'name' => 'Supplier 1',
'pic' => [
[
'name' => 'PIC 1',
'phone_number' => [
['number' => '111111'],
['number' => '123456']
]
],
[
'name' => 'PIC 2',
'phone_number' => [
['number' => '222222']
]
]
]
];
And this is my models
Supplier.php
// Supplier.php
public function supplier_pic()
{
return $this->hasMany('SupplierPIC');
}
and the other models
// SupplierPIC.php
public function supplier()
{
return $this->belongsTo('Supplier');
}
public function pic_phone_number()
{
return $this->hasMany('SupplierPICPhoneNumber');
}
// SupplierPICPhoneNumber.php
public function supplier_pic()
{
return $this->belongsTo('SupplierPIC');
}
How to save those data on controller ?
Thank you
You just need to break it down into it's constituent objects.
In your case, it is one Supplier object with two SupplierPIC objects, each of which has a SupplierPICPhoneNumber
Create Supplier
$supplier = Supplier::firstOrCreate([
'name' => 'Supplier 1'
]);
Create Supplier PIC(s)
collect($data['pics'])->each(function ($pic) use ($supplier) {
// Create the PIC
$x = SupplierPIC::create([
'name' => $pic['name']
]);
// Attach it to the supplier
$supplier->supplier_pic()->save($x);
// Attach phone numbers
collect($pic['phone_number'])->each(function ($number) use ($x) {
// Create the PIC Phone number
$y = SupplierPICPhoneNumber::create([
'number' => $pic['number']
]);
// Attach the number to the PIC
$x->pic_phone_number()->save($y);
});
});
Suggestions
The naming of your relationships doesn't follow best practice which is a little confusing. Try naming things that are use a hasMany type relationship with a plural (i.e. pic_phone_numbers rather than pic_phone_number)
Do you need an entire model for SupplierPICPhoneNumber? A json column may be better suited.

Laravel - adding relationships to a factory-created model

I am testing an eager loading relationship which contains many to many relations. Right now I have the queries and attachments within the test. I'm wondering if there is a way to move them into the factory, rather than including it as part of your test. This would limit the size of the test and then these relations could be created and used every time a film factory is created.
test
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$languages = Languages::where('name', 'english')->first();
$film->categories()->attach($categories->id);
$film->languages()->attach($languages->id);
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'name' => $film->name,
'description' => $film->description,
'categories' => $film->categories->toArray(),
'languages' => $film->languages->toArray()
}
filmFactory
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
If anyone could help with how i could do this or an example it would be great :D
You could use factory states and factory callbacks.
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->define(\App\Models\Category::class, function (Faker $faker){
return [
// Category fields
];
});
$factory->define(\App\Models\Language::class, function (Faker $faker){
return [
// Language fields
];
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-category', function (\App\Models\Film $film) {
$category = factory(\App\Models\Category::class)->create();
$film->categories()->attach($category->id);
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-language', function (\App\Models\Film $film) {
$language = factory(\App\Models\Language::class)->create();
$film->categories()->attach($language->id);
});
Then you can use in tests like this:
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$filmWithCategory = factory(Film::class)->state('with-category')->create();
$filmWithLanguage = factory(Film::class)->state('with-language')->create();
$filmWithCategoryAnLanguage = factory(Film::class)->states(['with-category', 'with-language'])->create();
// ...
}
PS: I don't recommend using existing data. From experience, I can tell you that can become really painful.
You can use factory callbacks to do it in the factory file:
<?php
use \App\Models\Film;
use \App\Models\Category;
use \App\Models\Languages;
$factory->define(Film::class, function(Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->afterCreating(Film::class, function(Film $film, Faker $faker) {
$category = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$language = Languages::where('name', 'english')->first();
$film->categories()->attach($category);
$film->languages()->attach($language);
});

updateOrCreate for polymorphic relation?

Is there a way to use updateOrCreate() or similar method for polymorphic relationship?
Problem with using updateOrCreate method I wouldn't know the id in the polymorphic table.
Currently doing like this:
if ($request->has('meta.description')) {
$description = $cat->tags()->where('key', 'description')->first();
if (is_null($description)) {
$cat->tags()->create([
'client_id' => client()->id,
'key' => 'description',
'value' => $request->input('meta.description'),
]);
} else {
$description->update([
'value' => $request->input('meta.description'),
]);
}
}
Tag method like like this Cat model:
public function tags()
{
return $this->morphMany('App\Tag', 'taggable', 'table_name', 'table_id');
}
You need to pass in a second parameter to updateOrCreate. Try splitting the array up into two like so:
$cat->tags()->updateOrCreate(
[
'client_id' => client()->id,
'key' => 'description'
],
[
'value' => $request->input('meta.description')
]
);

Laravel Model Factory get Same ID

How to get the same model id i tried this not working.
$factory->define(App\Item::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'color' => $faker->color_name,
'position' => App\Item::all()->get('id'),
];
});
I want to get the same id for the item so that the item position is the same when the application runs and can be edited to something (integer only).

Yii2 how to anchor the value of the relationship in the GridView

I am beginner. I explain the topic:
there is this relationship in the Ticket model:
public function getTyp()
{
return $this->hasOne(Typology::className(), [ 'id' =>'typ_id']);
}
and in the ticket table there is the typ_id column (it is in relationship with the id of the Typology table).
In the view views/ticket/index.php there is GridView::widgetwith these columns:
[
'attribute' => 'typ_id',
'value' => 'typ.typology'
],
I want to anchor the value of the relationship.
I have tried this:
[
'attribute' => 'typ_id',
'value' => function ($model) {
return Html::a (
'typ.typology',
'/typology/view?id='.$model->typ_id
);
}
]
but it doesn't work
someone can help me?
Html::a() interprets typ.typology as raw string. Use $model in value closure to get necessary property through relation.
Also instead of manually concatenate the url with its parameters, just pass them in array (see Url::to() to understand how link is constructed).
[
'attribute' => 'typ_id',
'value' => function ($model) {
return Html::a($model->typ->typology, ['/typology/view', 'id' => $model->typ_id]);
},
],

Categories