Hide specific columns from pivot key - laravel

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.

Related

Laravel 'hasMany' relationship not working, no idea why

I'm having trouble retrieving data through relationships.
My database structure (simplified):
orders:
id
user_id
product_id
order_items:
order_id
product_id
I need to get using relationships, all orders along with the items in the array.
Order model:
public function items()
{
return $this->hasMany(OrderItem::class, 'order_id', 'id');
}
Test controller:
public function test()
{
return Order::with('items')->get();
}
Result I got when accessing test():
[
{
"id": "d7baaae9-b925-4ff0-8bba-13e8e88d429b",
"user_id": "fa2a5f73-379d-4ab7-9bc5-81cdbd47f3b0",
"subtotal": "0.00",
"discount": "0.00",
"coupon_code": "0",
"total": "0.00",
"paid": false,
"refunded": false,
"created_at": "2022-07-26T16:41:50.000000Z",
"updated_at": "2022-07-26T17:51:45.000000Z",
"items": [
]
}
]
The "items" array does not exist in the orders table, it is coming through the relationship, but it comes empty. There is a record in the database relating orders with order_items, the OrderItem model is correctly accessing the database when I test. I don't know what the problem could be.
[EDIT_01]: I just found out that the problem is in the id I'm using, I'm using type Uuid (Ramsey\Uuid\Uuid\Uuid::uuid4()) for the keys of my tables, somehow it's not working, but when I testo with conventional ID works. Help-me.
I have the same issue, my fix is I add
protected $keyType = 'string';
to the table which has string id (uuid). Hope it can help
hope you found a solution, if not testing out your code sample here, and everything works fine for me,
public function items()
{
return $this->hasMany(OrderItem::class);
}
use this instead, should give you a better result
Try removing the third parameter and just use this:
return $this->hasMany(OrderItem::class, 'order_id');
This should work given your Database setup example, make sure you actually have items in your database that belong to that order you are debugging as well.

Getting the result of a trailing pivot table

Basically, I have three tables which are users, position, user_position and I want to get all users with their respective positions
Note that a single user can have multiple positions
The `user_position` table consists of:
- user_id
- position_id
To illustrate... it goes something like this:
users-->user_position<--position
What I got so far is this:
User Model
public function user_positions() {
return $this->hasMany('App\Models\UserPosition', 'user_id', 'id');
}
UserPosition Model
public function positions() {
return $this->hasMany('App\Models\Position', 'position_id', 'id');
}
UserController
public function getallusers() {
$data = User::with('user_positions')->get();
return response()->json($data);
}
So far it gives me the correct result set.
For example:
[{id:1,
name:'John',
user_positions:
[{id:1,
user_id:1,
position_id: 5
},
{id:2,
user_id:1,
position_id: 9
}
]
}]
However, it is incomplete, I also wanted the positions array inside the user_positions array but I don't know or I got lost on how to associate/merge/attach (i dont know the right term) the positions function to my User Model.
What you are looking for is a BelongsToMany relation.
You could add the following relation to your User model.
public function positions()
{
return $this->belongsToMany(Position::class);
}
You could eager load them with:
User::with('positions')->get();
Result:
[{
...
"positions": [{ ...The data of the position... }]
}]
When you want to m:m relation, it's advised to use BelongsToMany, there is no need for an intermediate model for the "pivoted" table.
If you have non-eloquent standard naming conventions. Then you have to specify the table name in the 2nd parameter as followed:
return $this->belongsToMany('App\Models\Positions', 'tbl_positions_assigned');
In the 3rd and 4th parameter you specify the foreign key names.
I'd like to refer to you to the documentation Eloquent-relationships as it's very thoroughly explained over there.
One very strong thing you can do with belongsToMany is to append extra columns in the pivot table. Those are also easily obtained from the pivot table. This is useful if you wish to store extra data values specific to the model.
return $this->belongsToMany('App\Models\Positions')->wherePivot('active', 1);
With above example you could exclude certain values, that are not active in this example, when you retrieve the results from the query.

How to get random row in object variable in Laravel?

Sorry for my bad english, I want to get a single row in my object. And I want that in random order. Im using array_rand() and it only return errors as stated below:
ErrorException: array_rand() expects parameter 1 to be array, object given in file C:\xampp\htdocs\user\TestProject\app\Http\Controllers\TestController.php on line
Here is my object.
"my_list": [
{
"id": 1,
"name": "My Name Test",
"address": [
{
"id": 1,
"city": "Manila",
"country": "Philippines"
}
]
},
{
"id": 2,
"name": "Your Name Test",
"address": [
{
"id": 2,
"city": "Cebu",
"country": "Philippines",
}
]
}
]
The problem is I want only to get a single row to the my_list which is object and not an array.
Here is my code.
$course = Course::where('id', 1)->with('my_list')->first();
$random_list = array_rand($course->my_list);
return $random_list;
I also try adding number of row in the array_rand like this.
$random_list = array_rand($course->my_list, 1);
But still not working.
What did I missed?
Any Eloquent query returns, by default, a Collection, even for the underlying relationships. Since you are working with one, this should work:
$course->my_list->random();
This will return only one item. If you want more, you could pass an argument to the random() method specifying the count of items you want.
For more information, check the documentation.
This Object is a Laravel collection. Please refer to the collection documentation.
https://laravel.com/docs/5.7/collections#method-random
You can try $course->my_list->random()
If you still wanna do this with your approach, can you try get_object_vars function to cast object into array.
$array = get_object_vars($object);
so that you can use them as an array in array_rand.
You might get an error, hence that it's an multi-dimensional array. Let me know so i may update.
Update for multidimensional:
Please refer to this.
// The second parameter of json_decode forces parsing into an associative array
$array = json_decode(json_encode($object), true);
try this:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder();
}])->first();
return $course->my_list;
Try this method:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder()->first();
}])->first();
return $course->my_list;
this method is more efficient since you will only get 1 row from my_list not like when you use $course->my_list->random() which retrieves all data and from there select a random row.
$random_list = $course['my_list']->random(number);
ps: number = number of element you want to get ,

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

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.

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