Laravel 5 can not access atributes from model - php

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.

Related

Laravel ManyToMany withPivot Trying to get property 'started_at' of non-object

I have a simeple ManyToMany relation between 2 models when I try to access a field in my pivot table I get this error:
Trying to get property 'started_at' of non-object
I have followed the instructions on the laravel documentation correctly.
My Models:
Instance:
public function steps(): BelongsToMany
{
return $this->belongsToMany(Step::class)
->using(InstanceStep::class)
->withTimestamps();
}
Step:
public function instances(): BelongsToMany
{
return $this->belongsToMany(Instance::class)
->using(InstanceStep::class)
->withPivot([
'started_at',
'finished_at',
'failed_at',
'output',
])
->withTimestamps();
}
How I try to access 'started_at' of a step:
dd($step->pivot->started_at);
EDIT
private function finishStep(Step $step)
{
dd($step->pivot->started_at);
}
/** #var Step $step */
foreach ($this->workflow->steps as $step) {
StepJob::dispatch($step, $instance);
}
dd($step)
Step {#420 ▼
#table: "workflow_steps"
#fillable: array:6 [▶]
#casts: array:3 [▶]
#connection: "mysql"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:10 [▼
"id" => "ca646f59-b215-41b0-afe6-ea9e6174f4f5"
"workflow_id" => "d786bd13-4111-4199-94d6-c52fef33b78b"
"category" => "transport"
"properties" => "props"
"entity_type" => "App\TransportWorkflowStep"
"entity_id" => "4d32e11c-6453-4f48-9419-7c5cbd647128"
"order" => 1
"created_at" => "2019-07-04 11:19:41"
"updated_at" => "2019-07-04 11:41:16"
"deleted_at" => null
]
#original: array:10 [▶]
#changes: []
#dates: array:1 [▶]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#guarded: array:1 [▶]
#forceDeleting: false
}
Attach step to instance
// set started_at
$workflowInstance->workflowsteps()->attach(
$step,
[
'started_at' => Carbon::now(),
]
);
// do something
// set finished_at when done
dd($step->pivot->started_at);
I did the dd($step->pivot->started_at); to see if i am working with the correct pivot row
You've not specified the relationship you're after, you may access the intermediate table using the pivot attribute on the models:
Try the following code:
private function finishStep(Step $step)
{
foreach ($step->instances as $instance) {
var_dump($instance->pivot->started_at);
}
die();
}
It depends on how declare $step, it gets collection and you have to insert it in foreach loop.
private function finishStep(Step $step)
{
dd($step->pivot->started_at);
}
this doesn't declare $step, you have to declare it first like
$step=Step::where(...)->get();
and then you can dd($step);
in short - object doesn't exist yet

How to get Laravel set table to 'stick' for collection

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;
}

Laravel count amount where the value of a key is true in multidemensional array

I have an array that looks like this when dd'd out :)
array:2 [▼
0 => Comment {#253 ▼
#guarded: []
#table: "comments"
+timestamps: false
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
#attributes: array:7 [▶]
#original: array:7 [▼
"id" => "1"
"on_projects" => "2"
"from_user" => "19"
"body" => "hi"
"unread" => "1"
"created_at" => "2016-06-13 23:54:39"
"updated_at" => "0000-00-00 00:00:00"
]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
1 => Comment {#269 ▶}
]
Now my goal is to count the amount of where the unread key is true.
This is what I've tried but doesn't seem to be working.
$unreads = $comments->where('unread', 1);
dd(count($unreads));
I get this error:
Call to a member function where() on array
Anyone can help me out here?
Many thanks in advance!
EDIT
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
//comments
if (!empty($projects)) {
foreach ($projects as $project) {
$comments_collection[] = $project->comments;
}
}
if (!empty($comments_collection)) {
$comments = array_collapse($comments_collection);
$unreads = collect($comments)->where('unread', 1)->count();
dd($unreads);
}
This gives me 0 it should give me 2 since I have 2 comments with unread 1
After you've retrieved the data you can collect, filter and count using Illuminate\Collection.
collect($comments)->where('unread', 1)->count();
That said, if your goal is to simply count the data and do nothing else with it, you could achieve this using Eloquent as presumably you've already used a similar query to get the data in the first place.
Comments::where('unread', 1)->count();
Amends based on OP edit.
$projects = Project::with(['comments'])
->where('user_id', Auth::id())
->get();
$count = $projects->sum(function ($project) {
return $project->comments->where('unread', 1)->count();
});
dd($count);
Having thought a bit more about it, as you just want a count (it seems), you can add a relation and an accessor to your Project model.
public function commentsCountUnread() {
return $this->hasOne(Comment::class)
->selectRaw('project_id, count(*) as aggregate')
->where('unread', 1)
->groupBy('project_id');
}
public function getCommentsCountUnreadAttribute()
{
if (! $this->relationLoaded('commentsCountUnread')) {
$this->load('commentsCountUnread');
}
$related = $this->getRelation('commentsCountUnread');
return ($related) ? (int) $related->aggregate : 0;
}
Then you could do something like
$posts = Project::with('commentsCountUnread')->get();
$count = $projects->sum(function($project) {
return $project->commentsCountUnread;
});
dd($count);

How to get an item from a collection in Laravel 5?

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;

Laravel save() method returning true but not updating records

I am using Eloquent to update my table Opportunity,
Opportunity Model
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Opportunity extends Model {
protected $primaryKey = 'OpportunityID';
protected $table = 'opportunitys';
// relationships
public function csfs()
{
return $this->hasMany('App\Csf', 'opportunityID');
}
public function benefits()
{
return $this->hasMany('App\Benefit', 'opportunityID');
}
public function risks()
{
return $this->hasMany('App\Risk', 'opportunityID');
}
public function projects()
{
return $this->belongsTo('App\Project', 'projectID');
}
public static function createNewOpportunity($input, $projectID)
{
$opportunity = new Opportunity;
$opportunity->value = $input['value'];
$opportunity->margin = $input['margin'];
$opportunity->duration = $input['duration'];
$opportunity->tender_type = $input['tender_type'];
$opportunity->likelihood_of_success = $input['likelihood_of_success'];
$opportunity->scope_of_work = $input['scope_of_work'];
$opportunity->deliverables = $input['deliverables'];
$opportunity->projectID = $projectID;
$opportunity->high_level_background = $input['high_level_background'];
if($opportunity->save())
{
Opportunity::leadSalesOppComplete($projectID);
return true;
};
}
public static function leadSalesOppComplete($projectID)
{
$task = Lead::where('projectID', '=', $projectID)->first();
$task->sales_opp = true;
return $task->save();
}
}
public function updateOpportunity(Request $request, $id) {
I get the id and find the opportunity.
$something = Opportunity::find($id);
I have died and dumped this and I get this
Opportunity {#259 ▼
#primaryKey: "OpportunityID"
#table: "opportunitys"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:12 [▼
"opportunityID" => 11
"value" => 0
"margin" => 0
"tender_type" => ""
"likelihood_of_success" => 0
"high_level_background" => ""
"scope_of_work" => ""
"deliverables" => ""
"duration" => ""
"projectID" => 6
"created_at" => "2015-03-11 17:45:47"
"updated_at" => "2015-03-11 17:45:47"
]
#original: array:12 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
}
Which is correct. I then update these with
$something->margin = $request['margin'];
$something->duration = $request['duration'];
$something->tender_type = $request['tender_type'];
$something->likelihood_of_success = $request['likelihood_of_success'];
$something->scope_of_work = $request['scope_of_work'];
$something->deliverables = $request['deliverables'];
$something->high_level_background = $request['high_level_background'];
Now if I die and dump I get
Opportunity {#259 ▼
#primaryKey: "OpportunityID"
#table: "opportunitys"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:12 [▼
"opportunityID" => 11
"value" => "25000"
"margin" => "0"
"tender_type" => "Proposal"
"likelihood_of_success" => "0"
"high_level_background" => ""
"scope_of_work" => ""
"deliverables" => ""
"duration" => ""
"projectID" => 6
"created_at" => "2015-03-11 17:45:47"
"updated_at" => "2015-03-11 17:45:47"
]
#original: array:12 [▼
"opportunityID" => 11
"value" => 0
"margin" => 0
"tender_type" => ""
"likelihood_of_success" => 0
"high_level_background" => ""
"scope_of_work" => ""
"deliverables" => ""
"duration" => ""
"projectID" => 6
"created_at" => "2015-03-11 17:45:47"
"updated_at" => "2015-03-11 17:45:47"
]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
}
I only changed the value which shows the change.
I now run
$something->save();
It returns true when I die and dump it.
But no records are changed in the database.
Any ideas?
two images from tinker
This line in the Opportunity model fixed the issue.
Protected $primaryKey = "opportunityID";
Although it is difficult to understand why it was still possible to retrieve the data and create a new record.
I had a very similar issue and this post lead me to a solution. I too was overriding a primaryKey.
Environment:
PHP 7.1
Lumen 5.3 (obviously Eloquent enabled)
Oracle 12 (using https://github.com/yajra/laravel-oci8)
SELECT and INSERT operations work with protected $primaryKey = 'USER_ID';, however, UPDATE operations did not work even though save() returned true.
After finding this post I changed case. protected $primaryKey = 'user_id';, Whammy, all three operations work! I wish I had a more solid explanation why this works. When I created the table I clearly used upper USER_ID VARCHAR2(50 BYTE) NOT NULL.
To answer the question in the title:
$user = \App\User::find(1);
$boolean = $user->save();
This will return true. Save always returns true, unless update or save did not succeed (because user_id is not found or something like this). Its not checked if an attribute has been changed. To see if an attribute has been changed see Laravel check if updateOrCreate performed update
Here is a bit of a Laravel eloquent Model.php :
// If the model already exists in the database we can just update our record
// that is already in this database using the current IDs in this "where"
// clause to only update this model. Otherwise, we'll just insert them.
if ($this->exists) {
$saved = $this->isDirty() ?
$this->performUpdate($query) : true;
}
// ...
return $saved;
isDirty() is true if model attribute has been changed and not saved. So if isDirty() is false, model will be not updated and return true.
I've had the same issue with Laravel 5.4 and MySQL. My original code was:
$attach = new Attachments ;
$attach->site_id = ($type == 'site' ? $typeId : null) ;
...
By adding a null primary id, save() behaves as expected.
$attach = new Attachments ;
$attach->id = null ;
$attach->site_id = ($type == 'site' ? $typeId : null) ;
...

Categories