many to many attach() says trying to get property of non-object - php

I have two database tables journeys and stops that are related in a many-to-many relationship. There is also the third table journey_stop (the pivot table) for the relationship.
Models
Here is my Journey.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Journey extends Model
{
public function stops() {
return $this->belongsToMany('App\Stop');
}
}
and the Stop.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Stop extends Model
{
public function journeys(){
return $this->belongsToMany('App\Journey');
}
}
Controller
Now in my controller, I have written a method changeStop(stop, journey_id) which takes a particular journey_id and either assigns a stop to it.(that is, creates a relationship between that particular stop and the journey) or removes the stop from the journey if it already exists.
Here is the method:
public function changeStop(Request $request, $id)
{
$stop = $request->all();
$journey = Journey::find($id);
if ($journey->stops()->contains($stop->id)) {
$journey->stops()->detach($stop->id);
}else{
$journey->stops()->attach($stop->id);
}
return $journey->stops();
}
But the line with the if statement throws the error:
Trying to get property of non-object
I have also tried using DB to query the pivot table directly but it throws the same error. Here's the code with DB:
public function changeStop(Request $request, $id)
{
$stop = $request->all();
$journey = Journey::find($id);
if (
DB::table('journey_stop')->where(
'journey_id',
$id
)->where(
'stop_id',
$stop->id
)->count() > 0
) {
$journey->stops->detach($stop->id);
} else {
$journey->stops->attach($stop->id);
}
return $journey->stops();
}
Everything seems right for me. But it doesn't work. What am I doing wrong?
Thanks for your time :)

You may also use the sync method to construct many-to-many associations. The sync method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the IDs in the given array will exist in the intermediate table
$journey->stops()->sync([$stop_id])
And to work for your above code try this:
public function changeStop(Request $request, $id)
{
$stop = $request->all(); // returns an array
$journey = Journey::find($id);
if ($journey->stops->contains('id', $stop['id'])) {
$journey->stops()->detach($stop['id']);
} else {
$journey->stops()->attach($stop['id']);
}
return $journey->stops;
}

Related

How to apply relationship with two id's in laravel

So I have a model called data_storage and another model entity_states
I have to fetch the record from data_storage with entity_states where entity_state has data_storage_id and state_id.
How can I use eloquent to achieve this ?.
Or Ill have to use Query builder and use innerJoin?
Update1
My Actual Query
$this->values['new_leads'] = $data_storages->with('actions','states','sla')->where('wf_id',$wfid)->get();
My data_storage modal
class data_storages extends Model
{
//
protected $fillable = ['layout_id','member_id','company_id','team_id','data','status','wf_id'];
function actions()
{
return $this->hasMany('App\Models\ActionDataMaps', 'data_id', 'id' );
}
function states()
{
return $this->hasOne('App\Models\workflow_states','id','status');
}
function sla()
{
//Here I have to get those row from entity_states model where , data_storage_id and state_id
}
}
Thanks
Here's the more reasonable way to do it:
class DataStorage extends Model {
public states() {
return $this->belongsToMany(State::class,"entity_states");
}
}
class State extends Model {
public storages() {
return $this->belongsToMany(DataStorage::class,"entity_states");
}
}
Then you can eager-load related models via e.g.:
$storage = DataStorage::with("states")->first();
$storage->states->first()->column_in_related_state;
Or via the state:
$state = State::with("storages")->first();
$state->storages->first()->column_in_related_storage;
If there are additional columns in the pivot table entity_states then you can refer to them in the relationship as e.g.:
public states() {
return $this->belongsToMany(State::class)->withPivot("pivot_column");
}
In your model data_storage you can define a property / method entity_states to get them:
class data_storage extends Model
{
public function entity_states()
{
return $this->hasMany('App\entity_states','data_storage_id')->where('state_id ','=',$this->table());
}
}
Then you can access them in an instance by
$entityStatesOfDataStorage = $yourDataStorageInstance->entity_states;
See this link:
https://laravel.com/docs/5.3/eloquent-relationships
for Query Builder you may use this:
DB::table('data_storage')
->join('entity_states','data_storage.data_storage_id','=','entity_states.state_id')
->get();
For your reference Laravel Query Builder

onetoMany relationships not working or printing the wrong data. Laravel 5.3

So, this is weird. I have been implementing ontoMany relationships between users and various data sets. The first one worked... sort of. I set up the pivot table and what not, the data is correct on both ends of the table but the result when laravel calls the data is not even close:
Let's take this show user data function:
public function show($id)
{
try {
$loc = UserEdit::findorFail($id);
$array = UserEdit::findorFail($id)->toArray();
//$prefs = Ministry_Prefs_User::find($id);
return view('UserEdit', compact('array', 'loc'));
} catch(\Exception $e) {
return \Redirect::route('users.index')
->withMessage('This user does not exist');
}
}
In the blade I print out the tags for their preferences:
Preferences:<br>
{{ $array['Preferences'] }}<br>
#foreach ($loc->prefs_user as $tag)
{{ $tag->tag }}<br>
#endforeach<br><br><br>
The first array prints what's stored in the original untouched user data from the old table I inherited. That's so I can compare and make sure I'm getting the right data from the pivot tables I had to generate from this info. Turns out that was a good idea, coz this is what's printing:
1A-4,1-2,1-3,2-3,3-6,4-7,6-11,8-6,8-10,9-4,7A-4
1A-1
1A-1
1A-1
1A-3
1A-4
1A-5
1A-7
1-1
1-1
1-6
1A-8
I can see no pattern other than alphabetical order? Why would this happen?
The next point is even weirder. Using the same code get's me no results at all from the other pivot tables I've set up:
public function country() {
return $this->belongsToMany('App\Country', 'country_user', 'user_id', 'country_id');
}
public function prefs_user() {
return $this->belongsToMany('App\Prefs_User', 'tag_user', 'user_id', 'tag_id');
}
public function language() {
return $this->belongsToMany('App\language', 'language_user', 'user_id', 'language_id');
Why would this happen? The models look like this:
Prefs_User
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Prefs_User extends Model
{
protected $table = 'prefs';
public function prefs_user() {
return $this->belongsToMany('App\UserEdit', 'tag_user', 'tag_id', 'user_id');
}
}
Country
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $table = 'countries';
public function country_user() {
return $this->belongsToMany('App\UserEdit', 'country_user', 'country_id', 'user_id');
}
//protected $fillable = ['region', 'country'];
}
language
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class language extends Model
{
protected $table = 'languages';
//protected $fillable = ['user_id','language_id'];
public function language() {
return $this->belongsToMany('App\UserEdit', 'language_user', 'language_id', 'user_id');
}
}
Ok. So this was a problem caused by a function of how Laravel works.
After some trial and error, I made a guess that Laravel only checks for id's when using pivot magic methods and does not search for any other data.
So I wrote a function to get the preferences, explode them out into the original tags, then compare that tag against the preference list, get the id associated with that tag and then print it to the pivot table with the other data. For testing and double checking purposes the original tag was initially included, then removed later.
public function move() {
//get the preferences from the users table (this is stored badly "1-7,3-4,4-6,6-6,6-10,8-5,8-9,8-10,8-13,9-3,9-9") this returns a collection
$prefs = DB::table('users')->select('id', 'Preferences')->where('Preferences', '!=', '')->get();
//iterate through each item in the collection
foreach ($prefs as $pref) {
//get the tags only
$tags = $pref->Preferences;
//break up the tags into a searchable array by exploding each preference at the comma
$tag = explode(',', $tags);
//for each tag in the array assign it to t
foreach ($tag as $t) {
//get the id from the preference table when the tag matches t
$new = DB::table('prefs')->select('id')->where('tag', $t)->first();
//make a new model instance to insert data
$taguser = new Tag(array(
'user_id' => $pref->id,
'tag_id' => $t,
'tags' => $new->id
));
//insert data
$taguser->save(); //save in table
//dd($taguser); <- Since we have a lot of items, dd will break the printout after the first, meaning you can check to see if your information is being handled properly
}
}
return view('refactor');
getting rid of the original tag-id and replacing it with just the plain id that referenced the tag meant that laravel could now search against the correct data and return useful results.

Get laravel data with relation between tables

I have the following data models:
class Cliente extends Model
{
public function sector()
{
return $this->belongsTo(Sector::class,'sectoresId');
}
}
class Sector extends Model
{
public function sectorLanguage()
{
return $this->hasMany(SectorLanguage::class,'sectoresId');
}
public function cliente()
{
return $this->hasMany(ClienteLanguage::class,'sectoresId');
}
}
class SectorLanguage extends Model
{
public function sector()
{
return $this->belongsTo(Sector::class,'sectoresId');
}
public function idioma()
{
return $this->belongsTo(Idioma::class,'idiomasId');
}
}
I want to recover all the active clients and the name of the sector to which it belongs, if I do something like this
$cliente = Cliente::where('active','1');
When I run $client I can not enter the attribute
foreach($cliente as $cli) {
$cli->sector->sectorLanguage->nombre;
}
Why? It only works for me when I find it by id
$cliente = Cliente::find(1);
echo $cliente->sector->sectorLanguage->nombre;
How can I get what I need without resorting to doing SQL with Query Builder.
Thank you very much, greetings.
According to your defined relations, the Sector has many sectorLanguage, it means you're receiving a collection, so you should work on an object like this:
$cliente->where('active', 1)
->first()
->sector
->sectorLanguage
->first()
->nombre;
Cliente::where('active', 1) and sectorLanguage gives you the collection, so you
should first get the first item from $cliente & sectorLanguage and then apply the
desired relationship
Hope it should work!

Laravel models and relations tickets with feedback and users

I am trying to grasp the concept of Eloquent ORM by creating a ticketing system at the moment. What I am trying to achieve is:
The tickets with the user who posted the ticket
The feedback belonging to the ticket and the user who entered the
feedback
This is what I have right now:
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
And the following models
// Ticket.php
class Ticket extends Eloquent {
protected $table = 'helpdesk_tickets';
public function feedback()
{
return $this->hasMany('Feedback');
}
public function user()
{
return $this->belongsTo('User');
}
}
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
// User.php
class User extends Eloquent {
protected $table = 'users';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
What I have now is the tickets, their related feedback and user who created the ticket. What I am trying to achieve now is to also get the user who created the feedback.
You need to fix the relation:
// User model
public function tickets()
{
return $this->hasMany('Ticket'); // adjust namespace if needed
}
Next add the relation:
// Feedback model
public function user()
{
return $this->belongsTo('User'); // namespace like above
}
then use eager loading:
// it will execute 4 queries:
// 1st for tickets
// 2nd for feedback
// 3rd for feedbacks' user
// 4th for tickets' user
$tickets = Ticket::with('feedback.user', 'user')->latest()->get();
you can then access the relations in a loop, like below:
#foreach ($tickets as $ticket)
{{ $ticket->title }} by {{ $ticket->user->name }}
#foreach ($ticket->feedback as $feedback)
{{ $feedback->content }}
#endforeach
#endforeach
What you want to do is create nested relations, just like Ticket add a belgonsTo relation on feeback
When you want to use it you can chain relations using the dot notation feedback.user
The code
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
public function user()
{
return $this->belgonsTo('User')
}
}
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->with('feedback.user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
EDIT:
Even though this would work, it will execute more queries than needed. See Jareks answer.
Original Answer:
First of all you need to get your relationships straightened, in User.php you should call the user relationship with HasMany.
public function ticket() {
return $this->hasMany('Ticket');
}
In modules.helpdesk.index you should now have a Ticket Collection since your attaching the $ticket variable to the view.
If you loop through this collection with a foreach loop then what you should get is a model each loop:
foreach($tickets as $ticket) {
// Prints the name property of the Ticket model
print $ticket->name;
// Since a ticket only belongs to ONE user then that means that you are trying to fetch a model
// What we're doing here is getting the User model via the relationship you made in the model Ticket.php and then getting the name.
print $ticket->user()->first()->username;
// Since a ticket can have MANY feedbacks that means were fetching a collection
// which needs to be broken down to models so we do that looping the collection.
// Here we are doing the same thing as with the User model except with a collection.
foreach($ticket->feedback()->get() as $feedback) {
$feedback->text;
}
}
You should definitely check out the Laravel API and see Collection and Model there. http://laravel.com/api/ You get alot of help from there when you get stuck, trust me :)
I hope this answered your question.

Laravel querying model relations

I'm still struggeling with the laravel Models. At first I tried doing it all using the tables, but thats not smart, I'll miss out on lots of the laravel functions.
I have the following setup
ProjectTwitterStatus links the projects and the twitter statuses.
TwitterStatus has all the details of a twitter status and has a unique ID ('posted at' datetime of tweet is among the details)
TwitterRetweets has the ID of the TwitterStatus - the actual retweet - and the tweet ID of the retweeted status
TwitterReplies has the ID of the TwitterStatus - that is the actual reply - and/or the user ID if not a reply to a status but to a user.
What I want? To get for each date (DATE(datetime)) the count of the statuses, retweets and replies, using the laravel model relations.
These are the models.
class ProjectTwitterStatus extends Eloquent {
protected $table = 'project_twitter_statuses';
protected $softDelete = true;
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
public function project() {
return $this->belongsTo('Project');
}
}
class TwitterStatus extends Eloquent {
protected $table = 'twitter_statuses';
public function twitterRetweet() {
return $this->hasMany('TwitterRetweet');
}
public function twitterReply() {
return $this->hasMany('TwitterReply');
}
public function twitterUser() {
return $this->belongsTo('TwitterUser');
}
public function projectTwitterStatus() {
return $this->hasMany('ProjectTwitterStatus');
}
}
class TwitterRetweet extends Eloquent {
protected $table = 'twitter_retweets';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
class TwitterReply extends Eloquent {
protected $table = 'twitter_replies';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
I got the count of the twitterStatuses using this:
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->groupBy(DB::raw('DATE(datetime)'))
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
I tried for example this to get the retweet count added but that has no effect (a reference to the model apears in the object -> array().
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->with(array('twitterRetweet' => function($query)
{
$query->count();
}))
->groupBy(DB::raw('DATE(datetime)'))
->take(10)
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
Can anyone point me in the right direction?
Not 100% sure how your intended solution is to be used - Assuming you simply want a count of the number of retweets related to twitterStatus?
$count = $twitterStatus->twitterRetweet()->count();
where $twitterStatus is an already retrieved model - not a collection.
if $twitterStatus is a collection to iterate through you can also eager load the related model using either with() or load()
Then you can iterate through each model in the collection - depends on how you wanted to use the results

Categories