Model hide mutators and nested fields - laravel

Here is my JSON return
{
"product": {
"id": 1,
"name": "Glory Cookware",
"barcode": "1234567891234",
"local_code_id": 1,
"category_id": 1,
"description": null
},
"calculations": {
"product_quantity_in_warehouses": 1580,
"avgSellingPriceBeforeTax": 901.7085714285713,
"avgSellingPriceAfterTax": 1027.9477714285713,
"creditPriceInWarehousesBeforeTax": 1530828,
"creditPriceInWarehousesAfterTax": 1745143.92
}
}
What I am trying to do:
In the Product.php model I am trying to hide calculations.creditPriceInWarehousesBeforeTax.
What I have tried:
I have tried to use toArray() method in the Product.php mode.
public function toArray()
{
$hidden = ['calculations.creditPriceInWarehousesBeforeTax'];
// Also tried ['creditPriceInWarehousesBeforeTax']
$this -> hidden = $hidden;
return parent::toArray();
}
Expected behavior VS what happened
The JSON result should not return the calculations.creditPriceInWarehousesBeforeTax but what happened is that it return with the JSON.
NOTE
calculations are a mutator that I have created to append it as an attribute.

Related

Return Single column from HasOne laravel from model

I have the user image saved on a different table and I want to have the following in User model
public function Image()
{
return $this->hasOne(UserImages::class, 'user_id', 'id')->latest();
}
The above relation returns the following.
"image": {
"id": 3,
"user_id": 1,
"image": "http://live.test/uploads/user/User-Oss8MewXVzHZCehHoOUgkdYoo3N1K0gYI9jY69ZsnyiHnqHsHv.png",
"is_primary": 1,
"created_at": "2021-04-12T08:01:47.000000Z",
"updated_at": "2021-04-12T08:01:47.000000Z"
},
I want to receive only image, how can I do that?
use value() method
$user->image()->value('image');
From Eloquent documentation
If you don't need an entire row, you may extract a single value from a record using the value method. This method will return the value of the column directly:
$email = DB::table('users')->where('name', 'John')->value('email');
You can set it as a user attribute.
public function getProfileImageAttribute()
{
return optional($this->image)->image;
//or
return $this->image->image ?? null;
//or
return $this->image->image ?? 'path/of/default/image';
}
now you can call it like this
$user->profile_image;
You can create another function inside your model and access the previous method like
public function image()
{
return $this->hasOne(UserImages::class, 'user_id', 'id')->latest();
}
public function avatar()
{
return $this->image->image ?: null;
//OR
return $this->image->image ?? null;
//OR
return !is_null($this->image) ? $this->image->image : null;
//OR
return optional($this->image)->image;
}
And it will be accessible with $user->avatar();
As from discussion you are sending response to api
$this->user = $this->user->where('id', "!=", $current_user->id)->with(['chats','Image:user_id,image'])->paginate(50);
This will help you, but it will be better to use Resources for api responses to transform some specific fields.
You could use the magic Laravel provided:
$user->image->pluck('profile_image');
Documentation: https://laravel.com/docs/8.x/collections#method-pluck

Laravel: Cannot reindex collection's array in eager loading after using unset()

I have following code:
// User.php
public function groups() {
return $this->belongsToMany(
Group::class,
'group_user',
'user_id',
'group_id',
'id'
);
}
// Group.php
public function users() {
return $this->belongsToMany(
User::class,
'group_class',
'group_id',
'user_id',
'id'
);
}
And in routes/web.php
Route::get('/test', function () {
$me = App\User::first();
$group = App\Group::with('users')->first();
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
}
return $group;
}):
Result:
{
"id": 1,
"name": "ABC Group",
"users": { // This should be array right?
"1": { // This should be start with 0
"id": 2,
"name": "...",
"email": "...",
},
"2": { // This should be 1
"id": 3,
"name": "...",
"email": "...",
}
}
}
What I have tried:
#1 Put values() in the end of foreach loop, like:
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
$group->users->values(); // Not working
}
#2 Put values() after the foreach loop, like:
Route::get('/test', function () {
$me = App\User::first();
$group = App\Group::with('users')->first();
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
}
$group->users->values(); // Still not working
return $group;
}):
Expected result:
{
"id": 1,
"name": "ABC Group",
"users": [ // Array
{ // index 0
"id": 2,
"name": "...",
"email": "...",
},
{ // index 1
"id": 3,
"name": "...",
"email": "...",
}
]
}
Q: How to reindex collection array in eager loading after using unset()?
Thanks in advance
You've got a few things to unpack here that might help you.
First, your query returns a Laravel collection of users attached to the single model Group. Laravel has a bit of magic in the background that allows for array notation as well, but probably easiest to think about this as a collection for your purposes. In some cases, you can translate this to an array using Laravel's toArray() method, something like:
$userArray = $group->users->toArray();
For dropping an index, or in this case a user from the Group's users, take a look at the forget() method, which works on the collection object.
However, I think you may wish to come at this from the reverse... Pull the unwanted index(es) in a single query, rather than having to loop through the collection after the fact. Something like this may be of value to you:
$me = App\User::first();
$group = App\Group::with(['users' => function($query) use($me){
$query->where('users.id', '!=', $me->id);
}])->first();
This query will remove the unwanted user from the collection right out of the database, eliminating the need for additional code, which is what I think you were after.
HTH.

Join pivot has one array

I guys, i have 2 relations tables, and when listing all my messages, is shown the pivot columns relation, but i need to show the data has one array, is there a method in eloquent cant make this happen?
I searched and i no that is possible manipulate with collection methods but i wonder if there is another way.
My Model query is:
public function messages()
{
return $this->belongsToMany(Message::class,'message_users')->withPivot('is_read','sent_at');
}
This is how it is:
{
"data": [
{
"id": 4,
"title": "test",
"body": "<p>test</p>",
"pivot": {
"user_id": 1,
"message_id": 4,
"is_read": 0,
"sent_at": "2019-06-05 12:59:11"
}
}
]
}
This is how i want:
{
"data": [
{
"id": 4,
"title": "test",
"body": "<p>test</p>",
"user_id": 1,
"message_id": 4,
"is_read": 0,
"sent_at": "2019-06-05 12:59:11"
}
]
}
You can do next: in User model write toArray() method as
/**
* Convert the model instance to an array.
*
* #return array
*/
public function toArray(): array
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
// Detect if there is a pivot value and return that as the default value
if (isset($attributes['pivot'] && isset($attributes['pivot']['user_id']))) {
$attributes['user_id'] = $attributes['pivot']['user_id'];
$attributes['message_id'] = $attributes['pivot']['message_id'];
$attributes['is_read'] = $attributes['pivot']['is_read'];
$attributes['sent_at'] = $attributes['pivot']['sent_at'];
unset($attributes['pivot']);
}
return $attributes;
}

Laravel datatable nested row-details on nested relationships

I am trying to view laravel datatable with nested row details on nested relationships. Here are the details of what I have done
Database relationships
I want the datatable to view all products on page load, then first level of row detail will show all components of a product. Second level of row detail will show all sub-components from a component.
This is the current result of my datatable view
As shown in the illustration, the datatable fails to show the rawmaterial name of a sub-component.
Here are my Eloquent models and relationships.
Product Model
protected $table = 'products';
public function components() {
return $this->hasMany('App\Component', 'product_id', 'id');
}
Component Model
protected $table = 'components';
public function comp_subcomps() {
/*The syntax ->with('subcomponents') seems WORKING fine in resulting
the second level row detail ( viewing the subcomponents of a component )*/
return $this->hasMany('App\CompToSubComp', 'component_id', 'id')->with('subcomponents');
}
CompToSubComp Model
protected $table = 'comp_subcomp';
public function subcomponents() {
/* This syntax ->with('rawmaterials') seems NOT WORKING to
get the material name in the second level row detail */
return $this->belongsTo('App\SubComponent', 'subcomponent_id', 'id')->with('rawmaterials');
}
SubComponent Model
protected $table = 'subcomponents';
public function rawmaterials() {
return $this->belongsTo('App\Rawmaterial', 'rawmaterial_id', 'id');
}
CONTROLLER
/* On page load */
public function viewTable() {
$products = Product::select();
return Datatables::of($products)
->addColumn('details_url', function($product) {
return url('/product/getdetaildata/' . $product->id);
})->make(true);
}
/* First level row detail ( showing components of a product ) */
public function getDetailsData($id)
{
$components = Product::find($id)->components();
return Datatables::of($components)
->addColumn('moredetails_url', function($component) {
return url('/product/getmoredetail/' . $component->id);
})->make(true);
}
/* Second level row detail ( showing sub-components of a component ) */
public function getMoreDetailsData($id) {
$compsubcomp = Component::find($id)->comp_subcomps();
return Datatables::of($compsubcomp )->make(true);
}
JS DATATABLE
/* On load: Its working */
var dt = $('#example').DataTable({
...
"columns": [
{ "data": "id", "name": "id" },
{ "data": "name", "name": "name"}
]
});
/* First level row detail: Its working */
var tabComponent = $('#' + tableId).DataTable({
...
"columns": [
{ "data": "id", "name": "id" },
{ "data": "name", "name": "name" }
]
});
/* Second level: This PROBLEM exists where RAWMATERIAL NAME is not showing */
$('#' + tableId).DataTable({
...
"columns": [
{ "data": "id", "name": "subcomponents.id" },
{ "data": "name", "name": "subcomponents.name" },
{ "data": "rawmaterial_id", "name": "subcomponents.rawmaterial_id" },
{ "data": "subcomponents.rawmaterials.name", "name": "subcomponents.rawmaterials.name" }
]
});
How to correctly view the relationship between 'rawmaterials' and 'subcomponents' inside my datatable?
Any help to identify my errors would be appreciated. Thanks in advance.

Laravel 4.1 remove pivot attributes from response

I am using laravel 4.1 to build an api. I have pivot a table which is working fine. But the response comes with pivot attributes which i don't want. as you will see in my example i have to two tables name: trips and users. I don't want to see pivot table attributes in my response. Here is the example:
[
{
"id": 140,
"name_first": "hasan",
"name_last": "hasibul",
"profile_image": "/assets/images/default-profile-img.png",
"created_at": "2013-09-18 08:19:50",
"last_login": "2013-12-26 11:28:44",
"status": "active",
"last_update": "2013-10-15 13:40:47",
"google_refresh_token": null,
"is_admin": 1,
"updated_at": null,
"pivot": {
"trip_id": 200,
"user_id": 140
}
}
This is my User Model:
public function trips(){
return $this->belongsToMany('Trip');
}
This is my trip model:
public function users(){
return $this->belongsToMany('User');
}
This is my controller:
public function index($tripId)
{
$userCollection = Trip::find($tripId)->users;
return $userCollection;
}
This is my route:
//get all the users belongs to the trip
Route::get('trips/{tripId}/users', array(
'as' => 'trips/users/index',
'uses' => 'TripUserController#index'
));
is there any way i can remove pivot attributes using laravel or i have to use php ?
Use the $hidden property of the model, you can add attributes or relations to it and the pivot is basicly acts as a relation.
class Foo extends Eloquent
{
protected $hidden = array('pivot');
public function bars()
{
return $this->belongsToMany('Bar');
}
}
If you want to remove just any one column from the response, then you can try something like this:
In you Model:
public function toArray()
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
unset($attributes['pivot']['user_id']);
return $attributes;
}
This way you will get only attribute required.
You can add it to your "hidden" array. At Model page
protected $hidden = [
'pivot'
];
As mentioned above you can remove the pivot attribute from the response, by adding the following to the related model.
protected $hidden = [
'pivot'
];
Moreover, in case you want to select specific fields from the pivot to be displayed in the related user object you can add this to your controller using Laravel 5.8. This works also when you hide the pivot information with the above code snippet.
public function index(Trip $trip)
{
return $trip->users()->select(['trip_id'])->paginate();
}
and you will receive something objects where the trip_id is added to the user object.
{
"data": [
{
"id": 140,
"trip_id": 200,
"name_first": "hasan",
"name_last": "hasibul",
"profile_image": "/assets/images/default-profile-img.png",
"created_at": "2013-09-18 08:19:50",
"last_login": "2013-12-26 11:28:44",
"status": "active",
"last_update": "2013-10-15 13:40:47",
"google_refresh_token": null,
"is_admin": 1,
"updated_at": null,
"pivot": {
"trip_id": 200,
"user_id": 140
}
}
]
}

Resources