Manually add item to existing object [Laravel 5] - php

Here is what I try to do:
$q = Question::where('id',$id -> id)->get();
$q[] = $q->push([ 'test' => true]);
dd($q);
This will output:
Collection {#220 ▼
#items: array:3 [▼
0 => Question {#225 ▶}
1 => array:1 [▼
"test" => true
]
2 => null
]
}
So 'test' => true will append as a new key, but I want to insert it in Question so latter I can access to it like this with foreach $q -> test
So here is how I want access to item:
#foreach($q as $qq)
{{ $qq->test }}
#endforeach

It can be done by using setAttribute() function of Eloquent Model (https://github.com/illuminate/database/blob/master/Eloquent/Model.php).
As You can see it stores data in protected $attributes using setAttribute(), and when we do $SomeModel->some_field it uses magic method __get() to retrieve item by association from attributes array.
Here is the resolution to Your question:
$Question = Question::find($id);
$Question->setAttribute('test', 'blablabla');

Apart from setAttribute(), you can use put() refer to this post for one item. And map() for many items, refer to this post.

Related

Set key to difference key when get laravel relationship

I want to change my collection array key to language code.
Here is my code
$products = Product::has('languages')->paginate(20);
return response()->json($products);
This is my relationship
public function languages($lang_id = null)
{
if ($lang_id)
return $this->hasMany(ProductLang::class)->where('lang_id', $lang_id)->first();
return $this->hasMany(ProductLang::class);
}
This is what I get for now.
Illuminate\Database\Eloquent\Collection {#613 ▼
#items: array:2 [▼
0 => App\Models\Product\ProductLang {#614 ▶}
1 => App\Models\Product\ProductLang {#615 ▶}
]
}
Expected:
Illuminate\Database\Eloquent\Collection {#613 ▼
#items: array:2 [▼
en => App\Models\Product\ProductLang {#614 ▶}
zh => App\Models\Product\ProductLang {#615 ▶}
]
}
Thanks for any help..
There is a collection method called keyBy. Also, I woudn't use a conditional parameter in a relationship the way you pass $lang_id. I'd rather create two relationship methods.
So, you can do:
$products = Product::has('languages')
->paginate(20)
->getCollection()
->map(function ($product) {
return $product->languages->keyBy('lang_id');
});
return response()->json($products);
You could try and remap the key of the loaded relationship after the query like so:
$products = Product::has('languages')->paginate(20);
foreach($products as $product){
$product->setRelation('languages', $product->languages->mapWithKeys(function($language){
// Assuming the code is in the attribute `language_code`
return [$language->language_code => $language];
}));
}
return response()->json($products);
You can use mapWithKeys collection method.
As per Laravel Documentation:
The mapWithKeys method iterates through the collection and passes each
value to the given callback. The callback should return an associative
array containing a single key / value pair:
$keyed = $products->mapWithKeys(function ($item) {
return [$item['locale'] => $item]; // assuming 'locale' key
});
$keyed->all();
References:
Laravel -> Collections -> Method mapwithkeys
Laracast -> Change eloquent model relationship key

Laravel access directly to json array in blade without using foreach statement

in this result:
LengthAwarePaginator {#251 ▼
#total: 2
#lastPage: 1
#items: Collection {#246 ▼
#items: array:2 [▼
0 => ProductCategories {#247 ▼
...
#attributes: array:6 [▼
"id" => 1
"category_name" => "test"
"lang" => "fa"
"images" => "{"images":{"original":"\/uploads\/post_images\/2017\/1512736029.jpeg","300":"\/uploads\/post_images\/2017\/300_1512736029.jpeg","600":"\/uploads\/post_images\/2017\/600_1512736029.jpeg","900":"\/uploads\/post_images\/2017\/900_1512736029.jpeg"},"thumbnail":"\/uploads\/post_images\/2017\/300_1512736029.jpeg"} ◀"
"created_at" => "2017-12-08 10:47:56"
"updated_at" => "2017-12-08 12:27:10"
]
#original: array:6 [▼
"id" => 1
"category_name" => "test test"
"lang" => "fa"
"images" => "{"images":{"original":"\/uploads\/post_images\/2017\/1512736029.jpeg","300":"\/uploads\/post_images\/2017\/300_1512736029.jpeg","600":"\/uploads\/post_images\/2017\/600_1512736029.jpeg","900":"\/uploads\/post_images\/2017\/900_1512736029.jpeg"},"thumbnail":"\/uploads\/post_images\/2017\/300_1512736029.jpeg"} ◀"
"created_at" => "2017-12-08 10:47:56"
"updated_at" => "2017-12-08 12:27:10"
]
...
}
1 => ProductCategories {#248 ▶}
]
}
which i get from this query:
$categories = ProductCategories::paginate(10);
dd($categories);
i'm trying to access to "images" column data such as thumbnail and 300 or etc, when i use foreach this data and show that on table i can't access to them, for example:
{{$categories->images->thumbnail}}
but i get Undefined property error and i can't show that
How about you just cast that field to an array ...
class ProductCategories ...
{
protected $casts = [
'images' => 'array',
];
...
}
#foreach ($categories as $category)
{{ $category->images['thumb'] }}
#endforeach
Laravel 5.5 Docs - Eloquent - Mutators - Array and Json Casting
The variable $categories is a Collection, so you need to get each individual object to get its data. For this you'll need to use a foreach.
#foreach($categories as $category)
{{ $category->images }}
#endforeach
And to have access to thumbnail you'll need to decode the json string that you have in images. For that, you can use json_decode($category->images) function. This will return an array with thumbnail as key.
If you want to make it right, you should keep image related data in a separate table and use a relationship between category and image:
public function images()
{
return $this->hasMany(Image::class);
}
But since you're keeping images data as JSON string, the first thing you want to do is to cast it to array. Add this to the model:
protected $casts = [
'images' => 'array',
];
Then in Blade view:
#foreach ($categories as $category)
{{ $category->images['thumbnail'] }}
#endforeach

Laravel (5.3) pluck collection is returning other results than array

I've encoutered something strange. I'm making a selectbox, and i'm using the pluck method on a database model.
This piece of code:
$orgs = Organisation::pluck('name', 'id');
dd($orgs);
Gives me the following results:
Collection {#611 ▼
#items: array:6961 [▼
0 => "Test organisatie"
1 => "Name"
2 => "Another"
As you can see, the ID is not present.
Now when i make it into an array:
$orgs = Organisation::pluck('name', 'id')->toArray();
dd($orgs);
It gives the following results:
array:6961 [▼
1 => "Test organisatie"
3 => "Name"
19 => "Another"
The array is perfectly usable, i just don't understand why there's a difference.
--Edit:
When i use the collection in the select form helper, it does display the keys properly. Making me think it's a bug in the var dumper?
#Patrick Vd Pols
could you please try as below
Organisation::pluck('name','id')->all();

Add simple array to Collection

in laravel this method is return Collection type of result:
$all_currency = CurrencyType::lists('currency_type', 'id');
Result:
Collection {#687 ▼
#items: array:2 [▼
1 => "EUR"
2 => "CHF"
]
}
now and i want to add -1 => "USD" to that. but i cant do it. my solution create nested array into that. for example:
$all_currency->push (["-1"=>"11"]);
Result:
Collection {#687 ▼
#items: array:4 [▼
1 => "EUR"
2 => "CHF"
3 => array:1 [▼
-1 => "USD"
]
]
}
If you need to use array, you can try to convert collection to an array with toArray() method:
$all_currency = CurrencyType::pluck('currency_type', 'id')->toArray();
$all_currency['-1'] = '11';
If you need to use collection, use put() helper:
$all_currency = CurrencyType::pluck('currency_type', 'id')->put('-1', '11');
Also, lists() is deprecated and will be removed in future, use pluck() instead.
Did you try to cast the collection to array before adding the currency?
Otherwise, it might help to check out the interface of the Collection class. Since you didn't mention it, it could be Doctrine or Propel or something else?
You then might find out, that there is a special function for adding a key-value pair, since pushing a value, adds that value (an array in your case) to the collection which is not what you want.
There might be no way of adding a value to a specific key index -1, that all depends on which ORM you are actually using and it's implementation.
//edit:
It seems the correct method is put and the code should look like this:
$all_currency->push (-1, "11");
https://laravel.com/api/master/Illuminate/Support/Collection.html#method_put
Use the put() method on your collection:
$all_currency->put(-1, "USD");
You can see all available methods for collections here: Collections

Laravel 5 select only relationship data on hasMany relationship

I am building an application that has projects and projects have plot_types.
I want to be able to check if a plot_type exists under the current project.
I have the following code:
$testResult = $project->with(['plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
}])->first()
This produces the following MySQL:
select exists(select * from `projects` where exists (select * from `projects_plot_types` where `projects_plot_types`.`project_id` = `projects`.`id` and `name` = ?)) as `exists`
This SQL returns rows that are NOT related to the $project object. For example when I do dd($project) I get:
#attributes: array:11 [▼
"id" => "4"
"name" => "xxx"
"number" => "1234"
"builder" => "1"
"overall_budget" => "3456.00"
"start_date" => "2016-03-31"
"end_date" => "2016-04-30"
"created_date" => "2016-03-16 15:22:05"
"updated_date" => "2016-03-16 15:22:07"
]
Yet, when I do dd($testResult); it gives;
#relations: array:1 [▼
"plotTypes" => Collection {#767 ▼
#items: array:1 [▼
0 => ProjectsPlotTypes {#770 ▼
#table: "projects_plot_types"
#fillable: array:2 [▶]
+timestamps: false
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
#attributes: array:4 [▼
"id" => "1"
"project_id" => "1"
"name" => "TYPE 1 - VENTILATION"
"budget" => "324.67"
]
Notice, the project_id above shows 1. This is not related to the current project as the current project id is 4.
Why is this happening?
This is one of those potentially confusing parts of the ActiveRecord model. All your model instances contain the same methods used to retrieve model instances, so it easy think something should work one way when it really doesn't.
Calling $project->with(), this is the exact same as calling Project::with(). Even though you're calling with() on an instance of the project, it isn't going to restrict the loaded objects to only those related to your instance.
When you call $project->with(), the first thing it does is create a new query for all projects, and then adds in the eager loading. You then call first(), which just gets the first project record, and all its eager loaded objects.
To get the plot types for your specific project, you have a couple options.
Just query the relationship. $project->plotTypes() gives you a base query for all the plot types associated with your project. You can add your constraints and get the records from there.
$plotTypes = $project->plotTypes()->where('name', $row->plot_name)->get();
dd($plotTypes);
Load the related plot types with constraints:
// assume your project doesn't have any plottypes loaded yet
$project = Project::find(1);
// load the plottypes relation with constraints
$project->load(['plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
}]);
dd($project->plotTypes);
Filter the already loaded Collection of related plot types. $project->plotTypes has all the plot types related to your project, but you can use the where() method on the Collection (different than the where() on the query) to filter through the records in the Collection.
// assume your project already has all plotTypes loaded
$project = Project::with('plotTypes')->find(1);
// you just want to get a subset of those pre-loaded plottypes
$plotTypes = $project->plotTypes->where('name', $row->plot_name);
dd($plotTypes);
Use the whereHas method to do the filtering instead of the with
$testResult = $project->whereHas('plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
})->with('plotTypes')->first();
And moreover do you want to get all the related records or only the first record?
if all then change the first() to get()
Hope this helps

Categories