Matching factory values in Laravel 7 - php

Good Afternoon,
I'm trying to create a Laravel factory where 2 of the 'columns' have the same values every time its called and the rest of the factory can be random.
For instance, I have the following columns in my DB
name
email
phone_number
status_message
status_code
I currently have my factory as follows;
$factory->define(Brand::class, function (Faker $faker) {
return [
'name' => $faker->unique()->company,
'email' => $faker->companyEmail,
'phone_number' => $faker->phoneNumber
];
});
This part works perfectly, as it should, the problem is that each specific status message comes with an individual status code. Is there a way I could add an array of status messages with a status code and have the factory pick a set at random for that record?
The status code / messages are listed below in array format;
[
'3e2s' => 'tangled web',
'29d7' => 'get certified',
'2r5g' => 'art of war',
]
I hope this makes sense. any help would be greatly appreciated.

as i can understand u need to pick random from this array u mentioned in above
$factory->define(Brand::class, function (Faker $faker) {
$data = [
'3e2s' => 'tangled web',
'29d7' => 'get certified',
'2r5g' => 'art of war',
];
$statusCode = array_rand($data);
$statusMessage = $data[$statusCode];
return [
'name' => $faker->unique()->company,
'email' => $faker->companyEmail,
'phone_number' => $faker->phoneNumber,
'status_message' => $statusMessage,
'status_code' => $statusCode,
];
});

Related

There is nothing happening when store data for Laravel API

I making laravel API where i can store a new data where the value in body raw json. but when i try to send request using post, i got nothing but the status is 200 OK. when i chek my mysql there is no data inputed.
So, what should i do?
mysql data
Laravel Controller, and API,
// function in controller
use App\Models\ChartAge;
class ChartController extends Controller
{
public function saveChart(Request $request)
{
$data = $request->validate([
'entity' => 'required|string|max:10',
'code' => 'required|string|max:10',
'year' => 'required|int|max:10',
'under_age_15' => 'required|string|max:50',
'age_15_64' => 'required|string|max:50',
'age_65_over' => 'required|string|max:50',
]);
$values = ChartAge::create($request);
return response()->json(
[
'status' => true,
'message' => "the videos has been favorites",
'data' => $values,
],
201
);
}
}
//in api.php
Route::post("charts", [ChartController::class, 'saveChart']);
and here is when i tried to send request using postman.
because there is no error, i don't know what's wrong??
First double check your ChartAge model, does it have $fillable or not?
and Edit your code:
From
$values = ChartAge::create($request);
To:
$values = ChartAge::create($request->all());
Hope this will be useful.
With validation:
$data = \Validator::make($request->all(),[
'entity' => 'required|string|max:10',
'code' => 'required|string|max:10',
'year' => 'required|int|max:10',
'under_age_15' => 'required|string|max:50',
'age_15_64' => 'required|string|max:50',
'age_65_over' => 'required|string|max:50',
]);
if($data-> fails()){
return back()->withErrors($data)->withInput();
}
$values = ChartAge::create($request->all());
Do you set fillable fields in your 'ChartAge' model?
protected $fillable = ['entity','code','year'...];
Do you try to test code with disabling validation?
Please try to put dd($request) in the first row of the controller code.
Method create expects a plain PHP array, not a Request object.

How should I use factories in Laravel when I have default values in DB

I have a factory:
$factory->define(\App\MissingData::class, function (Faker $faker) {
$operations = Operation::all()->pluck('id')->toArray();
$operationId = $faker->randomElement($operations);
$operation = Operation::find($operationId);
$meters = $operation->meters->pluck('id')->toArray();
$arrStatus = ['Done', 'Undone'];
return [
'operation_id' => $operationId,
'meter_id' => $faker->randomElement($meters),
'date_ini' => $faker->dateTimeThisYear,
'date_end' => $faker->dateTimeThisYear,
'status' => $faker->randomElement($arrStatus),
];
});
In my migration, I have:
$table->string('status')->default('Undone');
When I want to insert an array in DB, I always prefer to use factory:
factory(MissingData::class)->create($missingData);
with
return [
'operation_id' => $measure->operation_id,
'meter_id' => $measure->meter_id,
'conso_prod' => $measure->conso_prod,
'date_ini' => $missingDataIni,
'date_end' => $missingDataEnd,
];
The wanted behaviour is to insert the status: 'Undone' configured in DB, but my factory will generate a fake status, so I will always have to send Undone status to my factory, which is not the point of using a DB default.
How am I supposed to manage this. Using factory to create and insert model is a good practice.
Using default in DB is also very practical, I believe they can be used both at the same time, but I don't see how should I do that.
Any idea ?
Your best bet is probably to default the status to undone then have a seperate state for done and any other status' that you may add.
$factory->define(\App\MissingData::class, function (Faker $faker) {
$operations = Operation::all()->pluck('id')->toArray();
$operationId = $faker->randomElement($operations);
$operation = Operation::find($operationId);
$meters = $operation->meters->pluck('id')->toArray();
return [
'operation_id' => $operationId,
'meter_id' => $faker->randomElement($meters),
'date_ini' => $faker->dateTimeThisYear,
'date_end' => $faker->dateTimeThisYear,
'status' => 'Undone',
];
});
$factory->state(\App\MissingData::class, 'done', fn() => ['status' => 'Done']);
Then when you want the status to be done you would use the state like this.
factory(\App\MissingData)->state('done')->create();

Laravel phpunit test nested eager loading

I have this nested relation im abit unsure how i assertJson the response within the phpunit test.
FilmController
public function show(string $id)
{
$film = Film::with([
'account.user:id,account_id,location_id,name',
'account.user.location:id,city'
])->findOrFail($id);
}
FilmControllerTest
public function getFilmTest()
{
$film = factory(Film::class)->create();
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'description' => $film->description,
'account' => $film->account->toArray(),
'account.user' => $film->account->user->toArray(),
'account.user.location' => $film->account->user->location->toArray()
]);
}
Obviously this isnt working because its returning every column for the user im a little unfamiliar with how you test nested relations with the code you need so im unsure with a toArray can anyone help out?
Testing is a place where you throw DRY (don't repeat yourself) out and replace it with hard coded solutions. Why? simply, you want the test to always produce the same results and not be bound up on model logic, clever methods or similar. Read this amazing article.
Simply hard code the structure you expect to see. If you changed anything in your model to array approach, the test would still pass even thou your name was not in the response. Because you use the same approach for transformation as testing. I have tested a lot of Laravel apps by now and this is the approach i prefers.
$account = $film->account;
$user = $account->user;
$location = $user->location;
$response->assertExactJson([
'description' => $film->description,
'account' => [
'name' => $account->name,
'user' => [
'name' => $user->name,
'location' => [
'city' => $location->city,
],
],
],
]);
Don't test id's the database will handle those and is kinda redundant to test. If you want to check these things i would rather go with assertJsonStructure(), which does not assert the data but checks the JSON keys are properly set. I think it is fair to include both, just always check the JSON structure first as it would likely be the easiest to pass.
$response->assertJsonStructure([
'id',
'description',
'account' => [
'id',
'name',
'user' => [
'id',
'name',
'location' => [
'id',
'city',
],
],
],
]);

HasMany - save association data

I need help. I have a two tables business_departments and companies with association type hasMany.
I need to modify companies list consisting in the department. Code was generated via bake, after that I modified it.
Controller.
$businessDepartment = $this->BusinessDepartments->get($id, [
'contain' => ['Companies']
]);
$companies = $this->BusinessDepartments->Companies->find('list')->where([
'Companies.active' => true,
'Companies.type IS NOT' => 'service',
'OR' => [
'business_department_id IS NULL',
'business_department_id' => $id
]
])->distinct('Companies.id');
if ($this->request->is(['patch', 'post', 'put'])) {
debug($this->request->getData());
$businessDepartment = $this->BusinessDepartments->patchEntity($businessDepartment, $this->request->getData(), ['associated' => ['Companies']]);
debug($businessDepartment);
if ($this->BusinessDepartments->save($businessDepartment)) {
$this->Flash->success(__('The business department has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The business department could not be saved. Please, try again.'));
}
$this->set(compact('businessDepartment', 'companies'));
Entity.
protected $_accessible = [
'name' => true,
'companies' => true
];
Table
$this->hasMany('Companies', [
'foreignKey' => 'business_department_id',
// Tried it
/*'dependent' => true,
'cascadeCallbacks' => true,
'saveStrategy' => 'replace'*/
]);
template.
echo $this->Form->control('companies._ids', ['options' => $companies, 'multiple' => true, 'class' => 'multiple-find']);
First save with added companies is success, but when I tried to modify companies list (And if try to save without changes) I get error.
Can I save via *._ids or I need to make a custom code for it?
Below debug($this->request->getData())
[
'name' => 'Office',
'companies' => [
'_ids' => [
(int) 0 => '21',
(int) 1 => '29'
]
]
]
But after patchEntity, instead of searching for companies and changing the business_department_id fields in them, patchEntity tries to create new companies and displays an error. Below is a fragment of screenshot.
debug($businessDepartment) and screenshot page
Thank you. I hope for quick answer.
Maybe someone will come in handy!
you have validation errors in your company related data, thats why you
cant save it, if you want to just use _ids as save try clearing
companies field in your $businessDepartment i.e.
$businessDepartment->unsetProperty('companies');
before patchEntity
Graziel

Laravel Nested Relationship on Factory for Testing

I got a problem, i have to admit i don't find any solution.
I'm actually developping some testing for functionnalities and Factories are blocking me.
First I'm trying to add with factories an Entity called "Tasklist" which contains one or many "sections" which contains one or many "actions".
I have a 3 level deep relationship.
Here are my factories:
$factory->define(\App\V2\Models\Tasklist::class, function (\Faker\Generator $faker) {
return [
'id_course' => \App\V2\Models\Program::all()->random(1)->id,
'id_event' => \App\V2\Models\Stage::all()->random(1)->id,
'id_course_rounds' => \App\V2\Models\ProgramRound::all()->random(1)->id,
'name' => $faker->word,
'display_name' => $faker->word,
'color' => 0,
'key' => str_random(16),
'auto_active' => 1,
'status' => 1,
];
});
$factory->define(\App\V2\Models\TasklistSection::class, function (\Faker\Generator $faker) {
return [
'id_tasklist' => function(){
return factory(\App\V2\Models\Tasklist::class)->create()->id;
},
'number' => 1,
'title' => $faker->word,
'text' => $faker->text(100),
'status' => 1
];
});
$factory->define(\App\V2\Models\TasklistAction::class, function(\Faker\Generator $faker) {
return [
'id_tasklists_section' => factory(\App\V2\Models\TasklistSection::class)->create()->id,
'number' => rand(1, 10),
'title' => $faker->word,
'percent' => $faker->numberBetween(0, 100),
'status' => 1
];
});
In my testing class, i'm trying to generate a tasklist with 1 section with one action. The only way i found actually was something like that:
$task = factory(Tasklist::class, 2)->create()
->each(function($t){
$t->sections()->save(factory(TasklistSection::class)->create()
->each(function($s){
$s->actions()->save(factory(TasklistAction::class)->create());
})
);
});
To this code, if I delete the second each, it works, i got 2 tasklists with each 1 sections. In fact, the each is disturbing me.
I would like to create only one tasklist, with one or several sections with one or several actions on it.
But the each only accept Collection input the save method accepts only model input and not collection.
Does somebody have an idea how to deal with that ?
One approach can be this:
create task with sections and store them in the variable and then loop through each task section and add actions to it like this:
$tasklist = factory(App\Tasklist::class)->create();
$tasklist->sections()->saveMany(factory(App\TasklistSection::class, 3)->make());
foreach ($tasklist->sections as $section){
$section->actions()->saveMany(factory(App\TasklistAction::class, 3)->make());
}
this will work as expected.

Categories