using first() in laravel fractal - laravel

I want to transform on the first item of Contacts and here is my code
public function includeContact(Customer $customer)
{
return $this->item($customer->contacts()->first(), new ContactTransformer);
}
but it's not working and I get this error :
Type error: Argument 1 passed to
App\Transformers\ContactTransformer::transform() must be an instance
of App\Models\Contact, null given, called in
*\vendor\league\fractal\src\Scope.php
on line 407
Edited
Here is ContactTransformer
namespace App\Transformers;
use App\Models\Contact;
use League\Fractal\TransformerAbstract;
class ContactTransformer extends TransformerAbstract
{
public function transform(Contact $contact)
{
return [
'value' => $contact->value,
'type' => $contact->communication->title,
'icon' => $contact->communication->icon
];
}
}
Here is CustomerTransformer
class CustomerTransformer extends TransformerAbstract
{
protected $availableIncludes = ['contacts', 'contact'];
public function transform(Customer $customer)
{
return [
'id' => $customer->id,
'name'=>$customer->name,
'status' => $customer->status,
'tags' => $customer->tags->pluck('name'),
'created_at' => Verta::instance($customer->created_at)->format('Y/n/j'),
];
}
public function includeContacts(Customer $customer)
{
return $this->collection($customer->contacts, new ContactTransformer);
}
public function includeContact(Customer $customer)
{
return $this->collection($customer->contacts, new ContactTransformer);
}
}

It's because some of your customers don't have contacts. Looks at your ContactTransformer class on transform() method, it should receive instance of Contact. If you give that method null, of course it'll fail.
Then, you need to have like so,
class ContactTransformer extends TransformerAbstract
{
public function transform(Contact $contact = null)
{
if (is_null($contact)) return null;
return [
'value' => $contact->value,
'type' => $contact->communication->title,
'icon' => $contact->communication->icon
];
}
}

Related

How to test singleton(a class only new once) on laravel

This is Residence Facade:
class Residence extends Facade
{
protected static function getFacadeAccessor()
{
return 'residence.manager';
}
}
Here is the provider:
class ResidenceServiceProvider extends ServiceProvider implements DeferrableProvider
{
public $singletons = [
'residence.manager' => ResidenceManager::class,
'residence.japan' => ResidenceJapan::class,
'residence.us' => ResidenceUS::class,
'residence.eu' => ResidenceEU::class,
];
public function provides()
{
return [
'residence.manager',
'residence.japan',
'residence.us',
'residence.eu',
];
}
The ResidenceManager Class:
class ResidenceManager
{
protected $app;
public function __construct()
{
$this->app = app();
}
public function make($data)
{
$residenceService = match ($data['location']) {
'japan' => $this->app['residence.japan'],
'us' => $this->app['residence.us'],
'eu' => $this->app['residence.eu'],
default => false
};
if (!$residenceService ) {
return false;
}
return $residenceService->make($data);
}
}
I try to test if ResidenceJapan::class, ResidenceUS::class, and ResidenceEU::class only new once.
I use spy, bu it give me
Mockery\Exception\InvalidCountException
Method make(<Any Arguments>) from Mockery_0_App_Services_Residence_ResidenceJapan should be called
exactly 1 times but called 3 times.
/**
* #dataProvider residenceSeedProvider
*/
public function test_residence_class_only_new_once($data, $expected)
{
$residence= match ($data['location']) {
'japan' =>
[
'bind' => 'residence.japan',
'class' => ResidenceJapan::class
],
'us' =>
[
'bind' => 'residence.us',
'class' => ResidenceUS::class
],
'eu' => [
'bind' => 'residence.eu',
'class' => ResidenceEU::class
],
default => 'unknown',
};
$spy=$this->spy($residence['class']);
$this->app->instance(
$residence['bind'],
$spy
);
$i = 3;
while ($i > 0) {
//$this->assertNotNull(Residence::make($data));
Residence::make($data);// don't know why the Residence::make($data) return null
$i--;
}
$spy->shouldHaveReceived('make')->once();
}
So I use mock, but it give me
Error
Call to undefined method App\Services\Residence\ResidenceJapan::shouldHaveReceived()
/**
* #dataProvider residenceSeedProvider
*/
public function test_residence_class_only_new_once($data, $expected)
{
$residence= match ($data['location']) {
'japan' =>
[
'bind' => 'residence.japan',
'class' => ResidenceJapan::class
],
'us' =>
[
'bind' => 'residence.us',
'class' => ResidenceUS::class
],
'eu' => [
'bind' => 'residence.eu',
'class' => ResidenceEU::class
],
default => 'unknown',
};
$mock = Mockery::mock($residence['class'], function (MockInterface $mock) use ($expected) {
$mock->shouldReceive('make')->once()->andReturn($expected);
});
$this->app->instance(
$residence['bind'],
$mock
);
$i = 3;
while ($i > 0) {
$this->assertNotNull(Residence::make($data)); // the Residence::make($data) return what I am expected
$i--;
}
$residence['class']::shouldHaveReceived('make')->once();
}
How do I test if ResidenceJapan::class, ResidenceUS::class, and ResidenceEU::class only new once, no matter how many times the Facade Residence::make($data) calls?

How to set default value for a field in laravel `morphMany` relationship (not database tier)

I have a model File where save files of my app, it like:
class File{
public const IMAGE_TYPE = 'image';
public const AUDIO_TYPE = 'audio';
public const VIDEO_TYPE = 'video';
public const APPLICATION_TYPE = 'application';
protected $fillable = ['path', 'type', 'description', 'order', 'filable_type', 'filable_id'];
}
Suppose I have an Post model, it like:
class Post{
public function videos(){
return $this->morphMany(File::class, 'filable')
->where('type', File::VIDEO_TYPE);
}
public function images(){
return $this->morphMany(File::class, 'filable')
->where('type', File::IMAGE_TYPE);
}
}
When I get data of above relationships it's okay
But when I create a new file of post it is repetitive and easily make mistakes
$post->images()->create([
'path' => 'my-image.jpg',
'type' => File::IMAGE_TYPE,
]);
$post->videos()->create([
'path' => 'my-image.mp3',
'type' => File::VIDEO_TYPE,
]);
I want it look like:
$post->images()->create([
'path' => 'my-image.jpg',
]);
$post->videos()->create([
'path' => 'my-image.mp3',
]);
I don't need declare type per creating videos or images of a post.
How I can accomplish this!
Modal
// Change morphMany to hasMAny
public function videos()
{
return $this->hasMany(File::class, 'fileable')
->where('type', File::IMAGE_TYPE);
}
Controller
// You can do this
$vedioToCreate = $post->videos();
$vedioToCreate->path = 'my-image.mp3';
$vedioToCreate->save();
// Or you can do this
$post->videos()->create([
'path' => 'my-image.mp3',
]);

Laravel 8: Passing Factory properties into children relationships

we are currently working on a laravel 8 application. We are trying to create factories to create some dummy data for manual / developer based application testing.
The current code of my main Database-Seeder is below:
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call([
UserTableSeeder::class,
]);
\App\Models\User::factory(10)->create();
\App\Models\Activity::factory(5)->create();
/* 1. try
$tenFact = \App\Models\Tenant::factory(2)->has(
\App\Models\Project::factory(2)->state(
function (array $attributes, \App\Models\Tenant $tenant) {
return ['tenant_id' => $attributes['id']];
}
)->hasTasks(5)->hasLocation()
)->hasContracts(3)->create();
*/
/* Currently being used: */
\App\Models\Tenant::factory(10)->has(
\App\Models\Project::factory(5)->hasTasks(5)->hasLocation()
)->hasContracts(3)->create();
}
ProjectFactory.php:
class ProjectFactory extends Factory
{
protected $model = Project::class;
public function definition()
{
return [
'name' => 'Projekt: '. $this->faker->name,
'budget' => $this->faker->randomDigitNotNull*1000,
'progress' => $this->faker->randomDigitNotNull*10,
'budget_used' => $this->faker->randomDigitNotNull*50,
//'tenant_id' => Tenant::factory(),
'location_id' => Location::factory()->hasTenant(1),
];
}
}
LocationFactory.php:
class LocationFactory extends Factory
{
protected $model = Location::class;
public function definition()
{
return [
'name' => 'Standort: ' . $this->faker->company,
'street' => $this->faker->streetName,
'house_number' => $this->faker->buildingNumber,
'house_addition' => $this->faker->secondaryAddress,
'zip' => $this->faker->postcode,
'city' => $this->faker->city,
'tenant_id' => Tenant::factory(),
];
}
}
Our relationships look like this:
Tenant
|-- Project (has: tenant_id, but also has location_id)
| | -- Task (has: project_id)
|-- Locations (has: tenant_id)
|-- Contracts (has: tenant_id)
When creating datasets with the above named Tenant-Factory the following happens:
Tenant->id is being passed to Project(tenant_id)
but: Tenant->id is not being passend to Location (which depends on the tenants id but is also used for Project).
How can we pass the id of \App\Models\Tenant::factory(10) to Project::factory(5)->hasTasks(5)->hasLocation()?
Additionally we do have the problem, that even though we request 10 tenants, we will get around 60, because Location/Project create new objects when they should be using existing ones.
I gave up using the chained usage of the Tenant-Factory - I finally used some for-Loop that connected the related objects to each user by using laravels for() and state() methods:
for ($i=0; $i < 10 ; $i++) {
$tenant = \App\Models\Tenant::factory()->hasContracts(3)->create();
for ($j=0; $j < 5; $j++) {
$location = \App\Models\Location::factory(1)->for($tenant)->create();
$project = \App\Models\Project::factory(1)->state([
'location_id' => $location->first()['id'],
'tenant_id' => $tenant['id']])->hasTasks(5)->create();
}
}
class ProjectFactory extends Factory
{
$location_ids = App\Models\Location::pluck('id')->toArray();
protected $model = Project::class;
public function definition()
{
return [
'name' => 'Projekt: '. $this->faker->name,
'budget' => $this->faker->randomDigitNotNull*1000,
'progress' => $this->faker->randomDigitNotNull*10,
'budget_used' => $this->faker->randomDigitNotNull*50,
//'tenant_id' => Tenant::factory(),
'location_id'=> $faker->randomElement($location_ids),
];
}
}
class LocationFactory extends Factory
{
$tenant_ids = App\Models\Tenant::pluck('id')->toArray();
protected $model = Location::class;
public function definition()
{
return [
'name' => 'Standort: ' . $this->faker->company,
'street' => $this->faker->streetName,
'house_number' => $this->faker->buildingNumber,
'house_addition' => $this->faker->secondaryAddress,
'zip' => $this->faker->postcode,
'city' => $this->faker->city,
'tenant_id'=> $faker->randomElement($tenant_ids),
];
}
}

an added value of array of request disappears in Laravel Controller

the user id is existed Before doing create. so it causes an error in the first one.
I made it the other way. the second one below works correctly.
I would like to know why the first one is wrong and it's gone.
//Error
public function store(ContactRequest $request)
{
$request->user_id = $request->user()->id;
Log::debug($request->user()->id);
Log::debug($request);
Contact::create($request->all());
}
//OK
public function store(ContactRequest $request,Contact $contact)
{
$request->user_id = $request->user()->id;
$contact->title = $request->title;
$contact->body = $request->body;
$contact->user_id = $request->user()->id;
$contact->save();
}
the log of the first one is here.
What happened to the user_id!?
[2020-05-30 15:59:10] local.DEBUG: 59
[2020-05-30 15:59:10] local.DEBUG: array (
'_token' => 'gGWuxW6C2JRSCYDuCAC9HauynGclKQEQB7qUh6Rw',
'title' => 'TITLE',
'body' => 'MESSAGE',
'action' => 'SEND',
)
Contact is model class.
ContactRequest is here.
class ContactRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required|max:100',
'body' => 'required|max:1000'
];
}
public function attributes() {
return [
'title' => 'title',
'body' => 'CONTENTS'
];
}
}
You will have to use $request->merge(['user_id'=>$request->user()->id]).
Another tips is that you can simply use Auth::user()->id which also return the user id of current user.
What if you do this:
Auth::user() - >contact($request->all()) - >save() ;
Or also as an experiment:
$contact = new Contact($request->all()) ;
$contact->user_id = Auth::user() - >id;
$contact->save() ;
Actually the second snippet will surely work. The first one I did not test though it looks nice. :)

When collection->map returnes array then data in Collection raise error

In laravel 6 app I have collection defined as :
class PermissionCollection extends ResourceCollection
{
public static $wrap = 'permissions';
public function toArray($request)
{
return $this->collection->transform(function($permission){
return [
'id' => $permission->id,
'name' => $permission->name,
'is_checked' => !empty($permission->is_checked) ? $permission->is_checked : null,
'guard_name' => $permission->guard_name,
'created_at' => $permission->created_at,
];
});
}
}
I use it in a control, like :
$permissions = $user->permissions->all();
$userPermissionLabels= Permission
::get()
->map(function ($item) use($permissions) {
$is_checked= false;
foreach( $permissions as $nextPermission ) {
if($nextPermission->permission_id === $item->id) {
$is_checked= true;
break;
}
}
return [ 'id'=> $item->id, 'name'=> $item->name, 'is_checked' => $is_checked];
})
->all();
return (new PermissionCollection($userPermissionLabels));
and I got error :
Trying to get property 'id' of non-object
Looks like the reason is that collection->map returnes array of data, not objects.
If there is a way to fix it without creating new collection(using array) ?
MODIFIED :
I logged loging in my collection,
public function toArray($request)
{
return $this->collection->transform(function($permission){
\Log::info(' PermissionCollection $permission');
\Log::info($permission);
return [
'id' => $permission->id,
'name' => $permission->name,
'is_checked' => !empty($permission->is_checked) ? $permission->is_checked : null,
'guard_name' => $permission->guard_name,
'created_at' => $permission->created_at,
];
});
}
and I see in logs:
PermissionCollection $permission
array (
'id' => 1,
'name' => 'App admin',
'is_checked' => false,
)
local.ERROR: Trying to get property 'id' of non-object
The value is valid array, not null.
I mean I have already use this collenction in other part of the app, can I use it without creating a new one...
I think you get this error because you CollectionResource need to object of the Permission model, but in your case it is trying to get id from an array, after map function. Try to extend your model instead of returning an new array

Resources