Laravel Eloquent: how to add a column to a query response? - laravel

In other words, lets say i have a query like that:
return Location::valid()
->with('owners', 'gardeners')
->withCount('gardeners')
->get();
that returns some json :
[
{
name: 'LocationA',
nb_gardeners_max: 3,
owners: [...],
garderners: [...],
garderners_count: 2
}
]
in the json response i'd like to add some custom entry like is_full and available_places:
[
{
name: 'LocationA',
nb_gardeners_max: 3,
owners: [...],
garderners: [...],
garderners_count: 2,
...
is_full: false,
available_places: 1
}
]
I guess it has something to do with raw / join / aggregate... but i really don't understand how to do so. Any help will be greatly appreciated
EDIT
Minhajul answer is really usefull but it's not possible to do a WHERE clause on the appended attribute.
Location::where('is_full',true)->get() // NOT WORKING
Though it's still possible to do filter() on it, I'd like to use a JOIN for that to perform a single query

To do so, you can add attributes that do not have a corresponding column in your database.To do this, all you need to modify your model
public function getIsFullAttribute()
{
return $this->attributes['name'] == 'value';
}
After creating this accessor, you need to add this in your model.
protected $appends = ['is_full'];
Hopefully, it will help you.

Related

How to convert lavavel collection to square bracket collection

I have laravel livewire collection as below.
[
{
"date":"2021.09.01-0:00",
"open":110.177,
"close":110.175,
"low":110.172,
"high":110.18
},
{
"date":"2021.09.01-0:01",
"open":110.175,
"close":110.171,
"low":110.169,
"high":110.175
},
{
"date":"2021.09.01-0:02",
"open":110.171,
"close":110.173,
"low":110.17,
"high":110.176
}
]
I would like to convert them into form of square bracket collection without key name as below .
$data = [
['2021.09.01-0:00',110.177,110.175,110.172,110.18],
['2021.09.01-0:01',110.175,110.171,110.169,110.175],
['2021.09.01-0:02',110.171,110.173,110.17,110.176]
];
Any advice or guidance on this would be greatly appreciated, Thanks.
You can use collection map method:
The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it
https://laravel.com/docs/8.x/collections#method-map
$expectData = $collection->map(fn($item) => [$item-> date, $item->open,...])
I am not sure what the original data is but you can map over the collection and pull the values; if Eloquent Models:
$data->map(fn ($v) => array_values($v->toArray()))
Would depend upon how you got that original Collection for how it would end up in this scenario.
If you need to restrict what attributes and the order of them returned you can use only on the model:
fn ($v) => array_values($v->only(['date', ...]))

Can I directly get a nested list in my Doctrine result?

Sorry if the question is poorly phrased, I couldn't come up with a good way of describing my issue.
So basically, I'm using the Doctrine query builder to try and a list of training sessions from my Session entity. For each session, I need to fetch basic properties such as the name and date, but I also need to fetch the list of participants. There is a one-to-many relation between the entities Session and Participant, as there may be several participants to a given session. I would simply need a list of these participants, nested in each item of the list of sessions. Something like this:
[
{
"session_name": "name1",
"session_date": "date1",
...
"participants": [
{
"participant_name": "john1",
...
},
{
"participant_name": "john2",
...
},
...
],
},
{
"session_name": "name2",
"session_date": "date2",
...
"participants": [
{
"participant_name": "john3",
...
},
{
"participant_name": "john4",
...
},
...
],
},
...
]
This seems to me like it should be quite basic, but for the life of me I cannot get it to work with JOINs, subqueries, etc. The closest I got was this error, which does imply I'm trying to get a nested array (but it won't let me):
SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
I had this error running this code:
$query = $this->createQueryBuilder('s')
->select('
s.name,
s.date,
(SELECT p.nom FROM '.Participant::class.' p WHERE p.session = s.id) participants
')
->getQuery()->getArrayResult();
I know I could just fetch my sessions, then loop through them and fetch their participants with another DQL query, but obviously that doesn't seem like the proper way to do it. Is there a better way?
You can directly do a leftJoin on your QueryBuilder :
$queryResult = $this->createQueryBuilder('s')
// After that next line, you can reference participants as 'p'
->leftJoin('s.participants', 'p')
// If you intend to loop on the result, to avoid the N+1 problem, add this line :
->addSelect('p')
->getQuery()
->getResult()
;

Hide specific columns from pivot key

I have 2 tables say abc and xyz with ManyToMany relationship built over another table say abc_xyz (whose data will be returned as pivot key). However, pivot key upon retrieval has abc_id and xyz_id in return. I am able to access other columns in from abc_xyz table using method withPivot('dummy')
But, I want to hide the abc_id and xyz_id from the response. How do I do that?
I can hide the entire pivot key by using $hidden array but I want to hide only specific columns not the entire key.
Current Response
{
"abc_uuid": "some uuid",
"xyz" : [
{
"xyz_uuid": "some uuid",
"pivot": {
"abc_id": 1,
"xyz_id": 1,
"dummy" : "dummy value"
}
},
{
"xyz_uuid": "some uuid",
"pivot": {
"abc_id": 1,
"xyz_id": 2,
"dummy" : "dummy value"
}
}
]
}
So, I need only dummy from the pivot key, and hide abc_id and xyz_id. How do I do that?
Found a crude way to get this done. Found this answer in laravel issues unable to find the link now. However, it asks me to just add a method in my model and unset the keys I do not want.
public function toArray()
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
foreach($attributes['xyz'] as $key => $value) {
unset($value['pivot']['abc_id']);
unset($value['pivot']['xyz_id']);
$attributes['xyz'][$key] = $value;
}
return $attributes;
}
this unsets the unwanted keys from my response. I hope laravel gives out an easy way for this.

Why does Eloquent change relationship names on toArray call?

I'm encountering an annoying problem with Laravel and I'm hoping someone knows a way to override it...
This is for a system that allows sales reps to see inventory in their territories. I'm building an editor to allow our sales manager to go in and update the store ACL so he can manage his reps.
I have two related models:
class Store extends Eloquent {
public function StoreACLEntries()
{
return $this->hasMany("StoreACLEntry", "store_id");
}
}
class StoreACLEntry extends Eloquent {
public function Store()
{
return $this->belongsTo("Store");
}
}
The idea here is that a Store can have many entries in the ACL table.
The problem is this: I built a page which interacts with the server via AJAX. The manager can search in a variety of different ways and see the stores and the current restrictions for each from the ACL. My controller performs the search and returns the data (via AJAX) like this:
$stores = Store::where("searchCondition", "=", "whatever")
->with("StoreACLEntries")
->get();
return Response::json(array('stores' => $stores->toArray()));
The response that the client receives looks like this:
{
id: "some ID value",
store_ac_lentries: [
created_at: "2014-10-14 08:13:20"
field: "salesrep"
id: "1"
store_id: "5152-USA"
updated_at: "2014-10-14 08:13:20"
value: "salesrep ID value"
]
}
The problem is with the way the StoreACLEntries name is mutilated: it becomes store_ac_lentries. I've done a little digging and discovered it's the toArray method that's inserting those funky underscores.
So I have two questions: "why?" and "how do I stop it from doing that?"
It has something in common with automatic changing camelCase into snake_case. You should try to change your function name from StoreACLEntries to storeaclentries (lowercase) to remove this effect.

Create collection from input array

Lets say I have a form that is a invoice. It has line items like $product[$key], $quantity[$key]. So when the form is submitted the input looks like
{
customer_id : "214"
product_id: [ "1","5", "6" ],
quantity: ["34", "1", "54"]
}
I have a model for that details table. What I have been doing is iterating over it and creating a details object then saving it like this
foreach($product as $key=>$p)
{
if($p)
{
$t = new Details;
$t->product = $p;
$t->quantity = $quantity[$key];
$t->save();
}
}
I'm guessing there is a way to be much more efficient about this. Like creating a collection of details straight from the input but I have no idea how I would accomplish that
You can instantiate models through mass assignment.
$details = new Details(['product_id'=>'1','quantity'=>'34']);
You can also specify columns of the table that you do not want to be mass assigned using the $guarded variable in the model.
Check out mass assignment in Laravel's docs: http://laravel.com/docs/eloquent#mass-assignment
For your particular issue it looks like you would need to build your input array out of the elements of the other arrays.
Seems it isn't possible. Here is Taylor responding to a issue request. It seems the problem is it wouldn't be possible to fire events then. I just ended up doing
$d = array();
foreach ($details as $detail) {
$d[] = new OrderDetail($detail);
}
$order->details()->saveMany($d);

Resources