Can't transform results of Eloquent query - php

I'm building an API in Laravel to learn how to do such a thing. I'm following a Laracasts course to do this, but I'm having some troubles with the parts I want to do for myself.
Currently, I have this function in my controller. It fetches data from two tables and then returns it.
public function lesson($userid)
{
$lessons = DB::table('lessons')
->join('userlessons', 'lessons.id', '=', 'userlessons.lessonsid')
->select('lessons.name', 'lessons.seen')
->where('userlessons.userid','=', $userid)
->get();
return $this->respondWithPagination($lessons, [
'data' => $this->LessonTransformer->transformCollection($lessons)
]);
}
And LessonTransformer is this:
class LessonTransformer extends Transformer
{
public function transformCollection($items)
{
return array_map([$this, 'transform'], $items);
}
public function transform($item)
{
return [
'name' => $item['name'],
'seen' => (bool) $item['seen']
];
}
}
I tried a lot of solutions, some smart, some stupid. But I keep getting this error: Cannot use object of type stdClass as array

If you get such error, probably you need to change:
return [
'name' => $item['name'],
'seen' => (bool) $item['seen']
];
into:
return [
'name' => $item->name,
'seen' => (bool) $item->seen
];
but you haven't showed in which line error appear so it's only a guess

Related

Laravel API Resource not returning correct value with condition

I am new to Laravel API, I want to return a book where recommended === 1
In my resources, I have this
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
// 'image' => asset('/storage/'.$this->image),
'image' => $this->image_url,
// 'recommended' => $this->recommended,
'recommended' => $this->when($this->recommended === 1, $this->recommended),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
I want to return books when recommended === 1
My table is Like this
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('about');
$table->string('image');
$table->string('image_url');
$table->string('epub_url');
$table->integer('author_id');
$table->string('publisher');
$table->year('year');
$table->boolean('recommended')->default(0);
$table->timestamps();
});
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
UPDATE
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
Normally I will get all Books or Post like this using API Resource
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
'image' => $this->image_url,
'recommended' => $this->recommended,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
and call it like this in my controller
public function indexapi()
{
return BookResource::collection(Book::with('author')->Paginate(16));
}
But there some cases recommended is == 1 and some recommended == 0, in this case, I want to return data only when recommended == 1
I know my question is quite confusing
Thanks.
Thanks.
If I get it right, you want to filter & get only the books with ( recommended attribute == 1 ). If thats the case you shouldn't do it in your Collection file. You should do this filtering process in your Controller before passing any data to Collection.
Here is some code example from one of my project.
In ProductController.php FILE
public function index()
{
return new ProductCollection( Product::where('recommended','1')->get() );
}
As you can see , I'm filtering the products to get only the recommended ones. Then I'm sending this filtered data to the ProductCollection. This way The collection will only return the data I want.
In ProductCollection.php FILE
public function toArray($request)
{
return [
'data' => $this->collection->map( function($data) {
return [
'id' => (integer) $data->id,
'name' => $data->name,
'category_id' => $data->category_id,
'brand_id' => $data->brand_id,
'photos' => json_decode($data->photos),
'gtin' => $data->gtin
];
})
];
}
I don't have to make any changes in Collection. Because in this way , Collection should do the job for every data it gets.

Eager Load Pivot in nested BelongsToMany with Api Resource

I need your help!
I'm having problems returning pivot table information when using ApiResources.
If I have a model like this:
Post.php
public function likes()
{
return $this->belongsToMany(Like::class)
->withPivot(['points']) // I want this in my PostResource::collection !
}
When defining its Resources:
LikeResource.php
public function toArray($request)
{
return [
'like_field' => $this->like_field
];
}
PostResource.php
public function toArray($request)
{
return [
'title' => $this->title,
'likes' => LikeResource::collection($this->whenLoaded('likes'))
];
}
Then in PostController.php
return PostResource::collection(Post::with('likes')->get())
It will return something like this:
Controller Response
[
{
'title' => 'Post 1'
'likes' => [
{
'like_field' => 'Test'
},
{
'like_field' => 'Test 2'
}
]
},
{
'title' => 'Post 2',
...
}
]
The problem is, using that LikeResource::collection() it does not appends pivot information. How could I add 'points' of the pivot table when defining that PostResource??
Thats all,
Thx!
Solution
Well, simply reading a bit in Laravel Docs, to return pivot information you just has to use the method $this->whenPivotLoaded()
So, the PostResource becomes:
public function toArray($request)
{
return [
'title' => $this->title,
'likes' => LikeResource::collection($this->whenLoaded('likes')),
'like_post' => $this->whenPivotLoaded('like_post', function() {
return $this->pivot->like_field;
})
];
}

Laravel DataTables Service implementation and Joins

Can't figure out simple task: join 1 table and add column. There is no useful documentation about service implementation here: DataTables as a Service Implementation
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select($this->getColumns());
return $this->applyScopes($query);
}
protected function getColumns()
{
return [
'manufacturers.id',
];
}
Above will trigger bizarre error
Requested unknown parameter 'manufacturers.id' for row 0, column 0
Tried many variations like:
return [
'id',
];
Above will trigger Column 'id' in field list is ambiguous
Another one was:
return [
[
'name' => 'id',
'data' => 'id'
]
];
this will result in: strtolower() expects parameter 1 to be string, array given
And so on and so forth. Maybe some one just can give basic join example using Service implementation?
System details
Operating System OSX
PHP Version 7.2
Laravel Version 5.5
Laravel-Datatables Version 8.0
Update #1
This one seams to be closest to working solution:
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select( $this->getColumns() );
return $this->applyScopes($query);
}
and
protected function getColumns()
{
return [
'technika.id',
'manufacturers.title'
];
}
But I'm getting Requested unknown parameter 'technika.id' for row 0, column 0.
However XHR response seams to be ok, I can see correct ata coming from backend.
Solved problme by doing this:
protected function getColumns()
{
return [
[ 'data' => 'id', 'name' => 'technika.id', 'title' => 'ID' ],
[ 'data' => 'title', 'name' => 'manufacturers.title', 'title' => 'Manufacturer' ]
];
}
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select( collect($this->getColumns())->pluck('name')->toArray() );
return $this->applyScopes($query);
}
getColumns method is used in query() and in html() and they both expect different type of array format. So easest way is to extract name key ant put it to query() select method.
hope this will works for you if not then let me know
$query = Technika::query()
->select($this->getColumns())
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->get();
return $this->applyScopes($query);
protected function getColumns()
{
return 'manufacturers.id'
}

Using Fractal's $defaultIncludes

I am trying to use a $defaultIncludes() and am getting an exception --
ErrorException in ViewoptionTransformer.php line 8:
Argument 1 passed to App\Transformers\ViewoptionTransformer::transform() must be an instance of App\Viewoption, boolean given
Following the tutorial (http://laravelista.com/build-an-api-with-lumen-and-fractal/) except I am using Laravel 5.1 not Lumen:
in User model, I have the hasOne relationship with Viewoption called viewoptions
In the UsersController, I eager load viewoptions
public function index(Manager $fractal, UserTransformer $userTransformer)
{
$records = User::with(['locations', 'viewoptions'])->get();
$collection = new Collection($records, $userTransformer);
$data = $fractal->createData($collection)->toArray();
return $this->respondWithCORS($data);
}
In the UserTransformer, I have the $defaultInclude and the includes method
protected $defaultIncludes = ['viewoptions'];
`public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->name,
'is_active' => (boolean)$user->is_active,
'is_admin' => (boolean)$user->is_admin,
'is_manager' => (boolean)$user->is_manager,
'role_id' => (integer) $user->role_id,
'email' => $user->email,
'phone' => $user->phone,
'full_sidebar' => (boolean)$user->full_sidebar
];
}
public function includeViewoptions(User $user)
{
$viewoptions = $user->viewoptions;
return $this->collection($viewoptions, new ViewoptionTransformer);
}`
Have a ViewoptionTransformer
`
use App\Viewoption;
use League\Fractal\Resource\Collection;
use League\Fractal\TransformerAbstract;
class ViewoptionTransformer extends TransformerAbstract {
public function transform(Viewoption $item)
{
//return $item;
return [
'id' => $item->id,
'user_id' => $item->user_id,
'voAgency' => (boolean)$item->voAgency,
'voBalanceDue' => (boolean)$item->voBalanceDue,
'voCloseDate' => (boolean)$item->voCloseDate,
'voCommitTotal' => (boolean)$item->voCommitTotal,
'voDistributor' => (boolean)$item->voDistributor,
'voDueDate' => (boolean)$item->voDueDate,
'voFeePercentage' => (boolean)$item->voFeePercentage,
'voRegion' => (boolean)$item->voRegion,
'voSeason' => (boolean)$item->voSeason,
];
}
}`
Worked with these and slight variations of these throughout the day yesterday and I can't rid myself of that exception.
Not all of your users.id corresponds to some viewoptions.user_id.
Just check it:
$records = User::with(['locations', 'viewoptions'])->get();
dd($records);
some viewoptions will be null or false or just undefined
Instead of using collection use item like so
public function includeViewoptions(User $user){
$viewoptions = $user->viewoptions;
return $this->item($viewoptions, new ViewoptionTransformer);
}`
I had a similar issue today, all my other uses of transformers had been with hasMany relationships. I was always instantiating the transformer with a collection of objects.
However, when using a transformer with a belongsTo relationship and instantiating the transformer with only one object (similar to how you are passing only one object from a hasOne relationship) I would get the same boolean given error.
In the end I solved the issue by using 'item' instead of 'collection' when instantiating the transformer.
Within your includeViewoptions function
Instead of using
return $this->collection($viewoptions, new ViewoptionTransformer);
try
return $this->item($viewoptions, new ViewoptionTransformer);

yii2:data of related model in Gridview

I have two models namely MedicineRequestEntry and MedicineRequest. MedicineRequestEntry is related to MedicineRequest via
public function getMedicineRequests()
{
return $this->hasMany(MedicineRequest::className(),
['medicine_request_entry_id' => 'id']);
}
Now in grid-view of MedicineReuestEntry I am trying to pull the data from MedicineRequest Model using the relation using two alternative ways
like
[
'attribute' => 'is_delivered',
'value'=> 'medicineRequests.is_delivered'
],
In this method I am getting the value as not set.
and another method:
[
'attribute' => 'is_delivered',
'value'=> '$data->medicineRequests->is_delivered'
],
In this method I am getting the error like:
Getting unknown property: app\models\MedicineRequestEntry::$data->medicineRequests->is_delivered
Now I need some help, what I am doing wrong here.
Thank you.
You should use a callback function, see the guide:
[
'value' => function ($data) {
$str = '';
foreach($data->medicineRequests as $request) {
$str .= $request->is_delivered.',';
}
return $str;
},
],
Or for the first result of the array:
[
'value' => function ($data) {
return $data->medicineRequests[0]->is_delivered;
},
],

Categories