Laravel phpunit test nested eager loading - php

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',
],
],
],
]);

Related

Select2 keep showing "Searching" in ajax with pivot (n-n) in Laravel Backpack 5 Pro

I have 2 models: Order and Product, each has belongsToMany with pivot set properly.
In OrderCrudController, I also use FetchOperation and make a fethProducts() function as below:
use \Backpack\CRUD\app\Http\Controllers\Operations\FetchOperation;
public function fetchProducts()
{
return $this->fetch([
'model' => \App\Models\Product::class,
'searchable_attributes' => ['name','sku']
]);
// return $this->fetch(\App\Models\Product::class); <-- I also tried this one
}
protected function setupCreateOperation()
{
CRUD::setValidation(OrderRequest::class);
// other fields
$this->crud->addField([
'name' => 'products',
'type' => 'relationship',
'pivotSelect' => [
'attribute' => 'name',
'ajax' => true,
],
'subfields' => [
[
'name' => 'quantity',
'type' => 'number',
],
],
]);
}
But it comes to unexpected behavior when I search the product, the select2 field remains "searching" though the request successfully retrieved the data.
screenshot - select2 field
screenshot - ajax results
PS: this field works perfectly without subfields, no vendor overrides etc., so I think I've set everything correctly.
Anyone can help?
This was asked two months ago but somehow I missed it, just noticed it today after someone opened an issue on the GitHub repository.
I am happy you guys found a solution for it but unfortunatelly I cannot recommend it as using the select2_from_ajax will miss the functionality to don't allow the selection of the same pivots twice, otherwise you will have undesired consequences when saving the entry.
I've just submitted a PR to fix this issue, I will ping you guys here when it's merged, probably by next Monday.
Cheers
I just came accross this exact problem and after quite some research and trials, i finally found a solution !
The problem seems to be related to the relationship field type inside the pivotSelect. Try to use select2_from_ajax instead and don't forget to set method to POST explicitly, that worked for me like a charm.
Here is what you might try in your case :
$this->crud->addField([
'name' => 'products',
'type' => 'relationship',
'pivotSelect' => [
'attribute' => 'name',
'type' => 'select2_from_ajax',
'method' => 'POST',
'data_source' => backpack_url('order/fetch/products') // Assuming this is the URL of the fetch operation
],
'subfields' => [
[
'name' => 'quantity',
'type' => 'number',
],
],
]);

Matching factory values in Laravel 7

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,
];
});

Get specified properties of a model all at once in Laravel

I want to pass some user data to a view so it can be displayed in the profile page. There is quite a lot of it but I don't want to just pass everything, because there are some things the view shouldn't have access to. So my code looks like this:
return view('profile', [
'username' => Auth::user()->username,
'email' => Auth::user()->email,
'firstname' => Auth::user()->firstname,
'country' => Auth::user()->country,
'city' => Auth::user()->city->name,
'sex' => Auth::user()->sex,
'orientation' => Auth::user()->orientation,
'age' => Auth::user()->age,
'children' => Auth::user()->children,
'drinking' => Auth::user()->drinking,
'smoking' => Auth::user()->smoking,
'living' => Auth::user()->living,
'about' => Auth::user()->about,
]);
My question is: Can this be written shorter/simpler?
Thanks!
EDIT:
I don't want this: {{ Auth::user()->firstname }} because there is a logic in a view, which is bad - I think, there should be just plain variables to be displayed, in view, not anything else.
So I'm looking for something like:
return view('profile', Auth::user()->only(['firstname', 'email', ...]));
You could create by yourself a method named like getPublicData and then return all those properties you need.
...
public function getPublicData() {
return [
'property_name' => $this->property_name
];
}
...
... and then use it in your controller/views. Maybe it's not an optimal solution, but you can isolate this thing in the model and avoid too much code in the controller.
Another advanced approach could be the override of the __get method. However, I am not the first in this case.
Hope it helps!
You don't need to pass these variable to the view, you can directly access them in the view using blade
<h1>{{ Auth::user()->username }}</h1>

Zf2 HydratingResultSet and Multiple Entities

Just was wondering if ZF2's hydrating resultset can hydrate multiple entities. Consider the snippet below:
$sql = new Sql($this->adapter);
$sqlObject = $sql->select()
->from([
'ART' => 'acl_roles'
])
->join([
'ARTT' => 'acl_role_types',
],
'ART.type_id = ARTT.id',
[
'ARTT.id' => 'id',
'ARTT.identifier' => 'identifier',
'ARTT.name' => 'name',
'ARTT.status' => 'status',
'ARTT.dateAdded' => 'date_added',
],
Select::JOIN_INNER
)
->where([
'ART.identifier' => $identifier,
])
->columns([
'ART.id' => 'id',
'ART.type_id' => 'type_id',
'ART.identifier' => 'identifier',
'ART.name' => 'name',
'ART.status' => 'status',
'ART.description' => 'description',
'ART.dateAdded' => 'date_added',
]);
Now if the query was on a single entity, I could do something like:
$stmt = $sql->prepareStatementForSqlObject($sqlObject);
$resultset = $stmt->execute();
if ($resultset instanceof ResultInterface && $resultset->isQueryResult()) {
$hydratingResultSet = new HydratingResultSet(new ArraySerializable, new EntityClass);
$hydratingResultSet->initialize($resultset);
return $hydratingResultSet->current();
}
However in my case I need the hydrating result set to be able to build and return multiple entities (namely AclRoleEntity and AclRoleTypeEntity). Is this something that is possible? If yes how (considering the result set being a flat array of combination of both entities). If no are there better alternatives to achieve this without using Doctrine/Propel?
Thanks
It's totally possible, you're just going to need a configured (possibly custom) Hydrator.
Your hydrator will need to know the logic to inject your parameters into your objects from a flat array, and how to reduce your object models back to a flat array on extraction.
You're probably looking at a few Hydrator Strategies or a hydrator naming strategy and potentially a combination of both.
With the correct hydrator, you can achieve what you're looking for.

In Laravel 4, How are the merging rulesets implemented?

I'm using the dwightwatson/validating package to create validation rules in the model.
I particularly like the custom rulesets you can create for different routes.
Model
protected $rulesets = [
'set_up_all' => [
'headline' => 'required|max:100',
'description' => 'required'
],
'set_up_property' => [
'pets' => 'required'
],
'set_up_room' => [
'residents_gender' => 'required',
'residents_smoker' => 'required'
],
'set_up_roommate' => [
'personal_gender' => 'required',
'personal_smoker' => 'required'
]
];
Controller
$post = new Post(Input::all());
if($post->isValid('set_up_all', false)) {
return 'It passed validation';
} else {
return 'It failed validation';
}
In the above example, it works well in validating against the set_up_all ruleset. Now I would like to combine several rulesets and validate against all of them together.
According to the documentation, the package offers a way to merge rulesets. I just can't figure out how to integrate the example provided into my current flow.
According to the docs, I need to implement this line:
$mergedRules = $post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property');
This was my attempt, but it didn't work:
if($mergedRules->isValid()) { ...
I get the following error:
Call to a member function isValid() on array
I also tried this, but that didn't work either:
if($post->isValid($mergedRules)) { ...
I get the following error:
array_key_exists(): The first argument should be either a string or an integer
Any suggestions on how I would implement the merging rulesets?
From what I can see - mergeRulesets() returns an array of rules.
So if you do this - it might work:
$post = new Post(Input::all());
$post->setRules($post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property'));
if($post->isValid()) {
///
}
I've released an update version of the package for Laravel 4.2 (0.10.7) which now allows you to pass your rules to the isValid() method to validate against them.
$post->isValid($mergedRules);
The other answers will work, but this syntax is nicer (and won't override the existing rules on the model).

Categories