**** UPDATE *******************************************************
I'm not sure why, but moving everything into the controllers index() method rather than having it in the show() method solved the problem. Don't know why it works, but it does.
Original Question:
Cant figure out what is wrong here, i have followed the docs, tried variations, etc... still no luck making this work correctly.
Model A: (Slide)
public function carousels() {
return $this->belongsToMany(Carousel::class)->withTimestamp()
}
Model B: (Carousel)
public function slides() {
return $this->belongsToMany(Slide::class)->withTimestamp()
}
Pivot Table
--------------------------------------------------
|carousel_id | slide_id | created_at | updated_at|
--------------------------------------------------
From Controller:
public function show(Carosuel $carousels) {
$carousels = $carousel->with('slides)->get();
return view('myView', compact(['carousels']));
}
In the following dump, the "slides" are not in the attributes object of the collection; however, they are in the relations object. Im not sure if this is normal behavior or if the slides array should be in the attributes object. If the latter is the case, then how does one go about making that happen?
$caousels dump:
Collection {#1320 ▼
#items: array:20 [▼
0 => Carousel {#798 ▼
#fillable: array:4 [▶]
#connection: "mysql"
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:7 [▼
"id" => 1
"name" => "Prof. Andre Gerlach"
"category" => "Dr."
"active" => 0
"deleted_at" => null
"created_at" => "2018-04-07 21:10:52"
"updated_at" => "2018-04-07 21:10:52"
]
#original: array:7 [▶]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"slides" => Collection {#1300 ▼
#items: array:5 [▼
0 => Slide {#1023 ▶}
1 => Slide {#1024 ▶}
2 => Slide {#1025 ▶}
3 => Slide {#1026 ▶}
4 => Slide {#1027 ▶}
]
}
]
#touches: []
+timestamps: true
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
1 => Carousel {#799 ▶}
2 => Carousel {#800 ▶}
In the View:
foreach($carousels as $carousel){
echo $carousel->name;
foreach($carousel->slides as $slide){
echo $slide->tag;
}
}
This throws the E_ERROR Invalid argument supplied for foreach() in view
If, however, I cast this to an array, it works. Problem is that i also need to paginate the results. Once i do, the slides array is again inaccessible.
public function show(Carosuel $carousels) {
// This works - slides are available to me in the view
$carousels = $carousel->with('slides)->get()->toarray();
// This DOESN'T work - slides are not available to me in the view
$carousels = $carousel->with('slides)->paginate(6)->toarray();
return view('myView', compact(['carousels']));
}
dump as array:
array:12 [▼
"current_page" => 1
"data" => array:6 [▼
0 => array:8 [▼
"id" => 1
"name" => "Prof. Andre Gerlach"
"category" => "Dr."
"active" => 0
"deleted_at" => null
"created_at" => "Apr 07 2018"
"updated_at" => "2018-04-07 21:10:52"
"slides" => array:5 [▼
0 => array:11 [▶]
1 => array:11 [▶]
2 => array:11 [▶]
3 => array:11 [▶]
4 => array:11 [▶]
]
]
1 => array:8 [▶]
2 => array:8 [▶]
3 => array:8 [▶]
4 => array:8 [▶]
5 => array:8 [▶]
]
"first_page_url" => "//localhost:3000/admin/carousel/show?page=1"
"from" => 1
"last_page" => 4
"last_page_url" => "//localhost:3000/admin/carousel/show?page=4"
"next_page_url" => "//localhost:3000/admin/carousel/show?page=2"
"path" => "//localhost:3000/admin/carousel/show"
"per_page" => 6
"prev_page_url" => null
"to" => 6
"total" => 20
The problem here is that, in the view, the slides key is no longer accessible. Throws following error (only if I use ->paginate()).
E_ERROR Trying to get property 'slides' of non-object
Everything I have read in the docs and from other sources show this to be a pretty basic operation. For the live of me, i cant figure out why its causing such an issue. I should be able to access this in the view by simply nesting a foreach.
Any help with this would be greatly appreciated.
In the following dump, the "slides" are not in the attributes object of the collection; however, they are in the relations object.
Yes this is normal.
This is where you have a problem:
public function show(Carosuel $carousels) {
// ...
return view('myView', compact(['carousels']));
}
Typehinting a model in a function is reserved for model binding. Which is not what you're doing here since you don't have an {id} or some other parameter in your route.
Remove the typehinting and call the model instead:
public function show() {
$carousels = Carousel::with('slides')->get();
return view('myView', compact(['carousels']));
}
If you'd like to remove the dependency from the method, you should do it in the constructor instead.
Edit:
While I don't see any other problem and the above solution should work, if it's true that ->toArray() works for you (but you still need pagination), you could just convert the underlying collection:
public function show() {
$carousels = Carousel::with('slides')->paginate();
$carousels->setCollection(collect($carousels->getCollection()->toArray()));
return view('myView', compact(['carousels']));
}
Can you try this?
Model B: (Carousel)
public function slides() {
return $this->hasMany(Slide::class)->withTimestamp();
}
Related
We have a Laravel model and sometimes we want to get data from it's default table, but other times from a different table. It all works from a data perspective, but when the collection is returned from the alternate table, the 'table' attribute still references the original table (even though the data is from the other one as expected).
If we dd() getTable(), it is correct:
$model = COUNTRY;
$table = "countires";
$org_id = 1;
$org_collection = $model->setTable('merge_' . $table)->get()
->where('organization_id', $org_id)->keyBy('id');
dd($model->getTable()); // *** THIS SHOWS THE 2nd TABLE AS EXPECTED
dd() ouput:
"merge_countries"
But with the exact same code, if we dd() the result the table attribute in the collection is still the 1st one ('orgs') and not 'merge_orgs' as expected even though the data is correctly coming from the 'merge_orgs' table:
$model = COUNTRY;
$table = "countires";
$org_id = 1;
$org_collection = $model->setTable('merge_' . $table)->get()
->where('organization_id', $org_id)->keyBy('id');
dd($org_collection); // *** THIS SHOWS THE 1st TABLE EVEN THOUGH THE getTable() ABOVE DOES NOT
dd() output:
Collection {#297
#items: array:1 [
3 => Country {#295
#connection: "common"
#table: "countries" <---- WHY IS THIS NOT 'merge_countries'???
#hidden: array:2 [
0 => "created_at"
1 => "updated_at"
]
#appends: array:1 [
0 => "level"
]
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:7 [
"id" => 3
"organization_id" => 1
"region_id" => 2
"name" => "Southpark"
"active" => 1
"created_at" => "2018-06-21 14:05:36"
"updated_at" => "2018-06-21 13:25:27"
]
#original: array:7 [
"id" => 3
"organization_id" => 1
"region_id" => 2
"name" => "Southpark"
"active" => 1
"created_at" => "2018-06-21 14:05:36"
"updated_at" => "2018-06-21 13:25:27"
]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
}
]
}
Any idea how to address this? We're not sure what else to even try at this point. Laravel if 5.6 if that matters.
OK, there is a fix reference and more detail at github.com/laravel/framework/issues/26058. In the interim you can also override the model newInstance method in the trait to work around it:
public function newInstance($attributes = [], $exists = false)
{
// Overridden in order to allow for late table binding.
$model = parent::newInstance($attributes, $exists);
$model->setTable($this->table);
return $model;
}
In my Laravel App, a poll has many poll options. I can create a collection of those options by simply running:
$result = Poll::find(1)->options()->get();
This query returns:
Collection {#387 ▼
#items: array:6 [▼
0 => PollOption {#388 ▼
#table: "poll_options"
#connection: null
#primaryKey: "id"
#keyType: "int"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:8 [▼
"id" => 1
"poll_id" => 1
"option_text" => "Never"
"responses" => 54
"is_public" => 0
"created_at" => null
"updated_at" => null
"deleted_at" => null
]
#original: array:8 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
1 => PollOption {#389 ▶}
2 => PollOption {#390 ▶}
3 => PollOption {#391 ▶}
]
}
Within my poll option, there is a column called responses, which returns an integer. I need to take the collection above and isolate the responses key/value pair.
Collection {#385 ▼
#items: array:6 [▼
"responses" => 54
"responses" => 20
"responses" => 10
"responses" => 123
"responses" => 33
"responses" => 11
]
}
The only() collection method does exactly what I need, but I can't figure out how to structure the query when working with Eloquent models:
$options = Poll::find(1)->options()->get();
$responses = $options->only('responses');
dd($responses->all());
Returns an empty collection because the responses value is nested within the PollOption object.
I've also tried flatten(), but that seems to have no effect in this scenario.
Is there an easier way to return a single key/value pair within an Eloquent model collection?
It's not actually possible to assign the same key to multiple values within a collection. I knew this, but I wasn't thinking the problem through properly.
For future devs: the pluck() method takes two arguments: the value you want to pluck, and a second value that gets assigned to the key. Using the scenario above, I was able to write:
$result = $poll->options()->pluck('responses', 'option_text'); //Value and corresponding key
This results in something like:
Collection {#386 ▼
#items: array:6 [▼
"Never" => 54
"Occasionally" => 21
"2–3 times a week" => 80
"4–5 times per week" => 33
"Daily" => 11
]
}
Which ultimately did what I need it to do. Amit Gupta's mapWithKeys() answer is also correct, and will land you in the same place.
simply this code is my get data solution, this models are relationships and my result is true but i can't get data from relationship and show that on view, for example;
$data = UserAccountNumber::with(['currency_type'])->orderBy('id', 'DESC')->paginate(50);
this code return below result:
LengthAwarePaginator {#585 ▼
#total: 2
#lastPage: 1
#items: Collection {#601 ▼
#items: array:2 [▼
0 => UserAccountNumber {#588 ▶}
1 => UserAccountNumber {#589 ▼
#table: "user_account_numbers"
#guarded: array:1 [▶]
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:9 [▼
"id" => 2
"user_id" => 17
"account_number" => "24234234"
"card_number" => "242423423"
"sheba" => "asdasdad"
"currency_type" => 5
"status" => 0
"created_at" => "2016-05-13 10:55:00"
"updated_at" => "2016-05-13 10:55:00"
]
#original: array:9 [▶]
#relations: array:1 [▼
"currency_type" => CurrencyType {#603 ▼
#table: "currency_type"
#fillable: array:3 [▶]
#dates: array:1 [▶]
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:7 [▼
"id" => 5
"user_id" => 1
"currency_type" => "EURO"
"currency_symbol" => "EUR"
"created_at" => "2016-04-17 12:07:47"
"updated_at" => "2016-04-17 12:07:47"
"deleted_at" => null
]
View:
in view i can get UserAccountNumber data, but i can not get currency_type and show that on view:
<table class='table-style' cellpadding='0' cellspacing='0'>
#foreach ($data as $key=>$contents)
<tr>
<td>{{$contents->card_number}}</td>
<td>{{$contents->card_number}}</td>
<td>{{$contents->seba}}</td>
<td>{{$contents->created_at}}</td>
<td>
{{$contents->currency_type->currency_type}}
</td>
</tr>
#endforeach
</table>
i get error for {{$contents->currency_type->currency_type}} on view.
Error:
Trying to get property of non-object
POST UPDATED:
class UserAccountNumber extends Model
{
protected $table = 'user_account_numbers';
protected $guarded = ['id'];
public function user()
{
return $this->belongsTo('App\Http\Models\User');
}
public function currency_type()
{
return $this->belongsTo('App\Http\Models\CurrencyType', 'currency_type', 'id');
}
}
First thing to improve is in your UserAccountNumber model. Your currency_type relation should be written like:
public function currencyType()
{
return $this->belongsTo(CurrencyType::class, 'currency_type', 'id');
}
Your call would then look like:
// using the currencyType() method from above
$data = UserAccountNumber::with('currencyType')
->orderBy('id', 'DESC')
->paginate(50);
You then can access the currency type in your with with:
#foreach ($data as $key=>$contents)
$contents->currencyType->currency_type
#endforeach
It seems some of the UserAccountNumber do not have currency_type attached to it. Add something like this to your code:
#if (isset($contents->currency_type))
{{ $contents->currency_type->currency_type }}
#endif
Or:
{{ $contents->currency_type->currency_type or 'There is not type' }}
I got a slight issue,for some reason I'm unable to access my attributes from a relationship model.
I keep getting the same error, 'Undefined property: Illuminate\Database\Eloquent\Collection::$season'
I have been staring at this all day and I couldn't find a solution for it.
<?php namespace App\Http\Controllers;
use App\Show;
use App\Episode;
class TestController extends Controller {
public function test(){
$shows = Show::where('active',1)->get();
foreach ($shows as $show) {
if($show->active == 1 && $show->airday == date('N'))
{
echo 'test';
$episode = $show->episodes()->orderBy('id', 'desc')->take(1)->get();
\Debugbar::info($episode);
//this is the line where he gives me
echo('New episodes avaible, Season '.$episode->season.' Episode '.$episode->episode.' From '.$show->name);
//end error
}
}
}
}
?>
Although if I do a print out of the $episode variable I can clearly see the properties I wish to access.
Illuminate\Database\Eloquent\Collection {#150
#items: array:1 [
0 => App\Episode {#241
#table: "episodes"
+timestamps: true
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
#attributes: array:6 [
"id" => "6"
"show_id" => "6"
"season" => "3"
"episode" => "15"
"created_at" => "2016-02-07 11:45:53"
"updated_at" => "2016-02-07 11:45:53"
]
#original: array:6 [
"id" => "6"
"show_id" => "6"
"season" => "3"
"episode" => "15"
"created_at" => "2016-02-07 11:45:53"
"updated_at" => "2016-02-07 11:45:53"
]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
]
At the moment I'm clueless whats wrong with the code.
I hope someone can point me in the right direction.
When you call ->take(1)->get() you're telling Eloquent (the builder, really) to give you back a Collection object with 1 item in it.
So when you're trying to access your $season property, you're actually trying to access it on a Collection object and not the Model that you think.
You can do a couple of things:
You can replace your ->take(1)->get() call with ->first(),
or you can treat your current $episode variable as a collection, and retrieve the actual episode you care about by accessing $episode[0],
or you can call first on your current $episode variable ($episode = $episode->first()) to get the first model object within it.
I'm new to Laravel and I cannot figure out how to get the value of an item from within the returned collection. Here is my code:
$aircraft = Aircraft::join('pams_shared.shared_aircraft', 'aircraft.shared_aircraft_id', '=', 'pams_shared.shared_aircraft.shared_aircraft_id')
->select('aircraft.*', 'shared_aircraft.*')
->where('aircraft.owner_id',Auth::user()->owner_id)
->where('registration',$reg)
->get();
I want to get the value from aircraft.aircraft_id but that's where I get stuck. I've tried:
$id = $aircraft->aircraft_id;
I've tried:
$id = $aircraft->aircraft.aircraft_id;
I've also tried:
$id = array_get($aircraft, 'aircraft_id');
But I'm getting null as a value. When I perform either a var_dump($aircraft) or dd($aircraft) I'm able to confirm that the data is there.
In my view I am able to access the value by looping through, but I need this data in my controller to perform a second query on another table.
Hope that makes sense. Cheers.
EDIT
When running dd($aircraft); I get this:
Collection {#239 ▼
#items: array:1 [▼
0 => Aircraft {#240 ▼
#fillable: array:8 [▶]
#table: "aircraft"
#primaryKey: "aircraft_id"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:16 [▼
"aircraft_id" => 5
"owner_id" => 2
"shared_aircraft_id" => 67443
"year" => 2012
"serial_number" => "C127RG3"
"registration" => "C-SMTH"
"created_by" => 2
"modified_by" => 2
"created_at" => "0000-00-00 00:00:00"
"updated_at" => "0000-00-00 00:00:00"
"aircraft_code" => "2072438"
"aircraft_mfr" => "CESSNA"
"aircraft_model" => "172RG"
"aircraft_type" => 4
"aircraft_eng" => 1
"aircraft_cat" => 1
]
#original: array:16 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#guarded: array:1 [▶]
#dates: []
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
}
]
}
When you use get() method on a query builder it would return an array no matter how many record it gets. So you can access it by pointing it by index. By the way do not forget to check for emptiness to overcome possible errors.
$id = 0;
$aircraft = Aircraft::join('pams_shared.shared_aircraft', 'aircraft.shared_aircraft_id', '=', 'pams_shared.shared_aircraft.shared_aircraft_id')
->select('aircraft.*', 'shared_aircraft.*')
->where('aircraft.owner_id',Auth::user()->owner_id)
->where('registration',$reg)
->get();
// now you can access properties
if(count($aircraft) > 0) $id = $aircraft[0]->aircraft_id;
If you execute the query with get() you will always get a collection as result. Even if you just need one model. The simplest way to change that is to use first():
$aircraft = Aircraft::join('pams_shared.shared_aircraft', 'aircraft.shared_aircraft_id', '=', 'pams_shared.shared_aircraft.shared_aircraft_id')
->select('aircraft.*', 'shared_aircraft.*')
->where('aircraft.owner_id',Auth::user()->owner_id)
->where('registration',$reg)
->first();
$id = $aircraft->aircraft_id;