Remove relationship between Eloquent records - laravel-5

I've created two classes extending Eloquent (contacts and tags), they have a ManyToMany relationship. I'm trying to create the method for un-tagging a contact, but am unable to find any documentation to tell how to remove the entry in the relation-table without deleting either the tag itself or the contact.
So far, I've tried
$contact = Contact::find($contact_id);
$tag = $contact->tags->where('id', '=', $id);
$tag->delete();
This only deletes the contact. It makes sense that it doesn't work, but I'm not sure what else to try. I don't want to delete the contact or the tag, just the relationship between the two.
I've also now tried:
$tag = Tag::find($id);
$tag->contacts->detach($contact_id);
This gives me the error:
BadMethodCallException in Builder.php line 2071: Call to undefined
method Illuminate\Database\Query\Builder::detach()
as well as
$tag = Tag::find($id);
$contact = $tag->contacts->find($contact_id);
$tag->contacts->detach($contact);
This gives me the error:
FatalErrorException in Tag.php line 34: Call to undefined method
Illuminate\Database\Eloquent\Collection::detach()
Both the Contacts and Tags classes extend Illuminate\Database\Eloquent\Model;

You can use detach for many-to-many relationships
http://laravel.com/docs/5.1/eloquent-relationships#inserting-many-to-many-relationships
You just pass in the ID of the Tag. Take note of the parentheses after "tags"
$contact->tags()->detach($id);
Since it's many-to-many you could do the reverse as well
$tag->contacts()->detach($contact_id);
Similarly, you can use attach to create relationships. Just guessing since you didn't know about detach that you probably could use attach as well. Attach can take a single ID or an array of IDs (plus some more advanced options)
$contact->tags()->attach($id);
$contact->tags()->attach([$id1, $id2, ...]);

Related

Error Call to a member function where() on array Laravel

I want to do filtering from the data that I display, but there is a problem when I add where to my data.
the plan in the future I want to add if isset $request name, date and others. but was constrained at this one point.
Thank you for helping to answer in advance
$matchs =Matchs::where('type', 'sparring')->where('status','Pending')->whereNull('deleted_at')->get()->toArray();
$data=[];
foreach ($matchs as $key) {
$lawan = Matchs::where('id', $key['id'])->first()->ToArray();
$pertandingan = Sparring::where('match_id', $key['id'])->first()->ToArray();
$dua_arah = MatchTwoTeam::where('match_id', $key['id'])->first()->ToArray();
$tim = Team::where('id', $dua_arah['home_team'])->first()->ToArray();
$transfer['name']=$tim['name'];
$transfer['city']=$lawan['city'];
$transfer['field_cost']=$pertandingan['field_cost'];
$transfer['referee_cost']=$pertandingan['referee_cost'];
$transfer['logo_path']=$tim['logo_path'];
$transfer['nama_lapangan']=$lawan['nama_lapangan'];
$transfer['date']=$lawan['date'];
array_push($data,$transfer);
array_push($data,$pertandingan);
}
$data->where('name', 'LIKE', '%'.'football'.'%')->get()->toArray();
$data = array_search('football', array_column($data, 'name'));
$tittle="Sparring";
return view('mode.sparring',[
'tittle' => $tittle,
'data' => $data,
]);
You are trying to call where in an array which is not possible.
As you can see in the first line of your code you are calling where method in your model class. Like Matchs::where('type', 'sparring'), this is possible because Matchs is a Model class.
Now you can run where even if you are using array. You can convert that day in collection and then use array on that collection.
As below:
collect($data)->where('name', 'football')->toArray();
Here collect() will convert the $data array to collectio and then run the where() method in collectio then toArray() will change it back to array. But unfortunately there is no like operator possible in collection class. See the list of available method in Laravel collection here: https://laravel.com/docs/8.x/collections#available-methods
There is a way to do what you are trying to do. As far as I understand you want to filter the Matches where the Team name has footbal in it. You can do it like this:
Matchs::where('type', 'sparring')
->where('status','Pending')
->whereNull('deleted_at')
->whereHas('team', function($team) {
return $team->where('name', 'LIKE', '%'.'football'.'%')
})
->get()
->toArray();
So, here we can get the only those Mathes that has the Team that has the name contains football.
Few suggestion for you as seems you are new in Laravel:
Model name should be singular instead of plural, so the model class Matchs should be Match. Your name for team's model is Team is correct.
Avoid using toArray() because you won't need it. When you call get() it will return object of collection which more readable and powerful then array in most cases.
The code I suggested to use the like using whereHas will only work if you have propery defined your team relation in your Matchs class. So, defining your relationships in model is also important. If you do so, you don't even need the for loop and all those where in other model in that loop. You can do it in one query with all the relationships.

Undefined relationship error in sorting of existence and non existence relationship using Eager Load in Laravel eloquent model

I have a Student model and a corresponding one to one mapping relationship to Result model.
I have an eligibleList array containing a list of student id whose marks are to be displayed. Some student have results while some does not have but i need to display all of them from the list.
I am able to retrieve and display students using the following:
$students = Student::with('result:student_id,marks')->whereIn('students.id', $eligibleList)->get();
foreach($students as student) {
if ($student->result != null)
Log::debug($student->result->marks)
else
Log::debug("-1") //-1 indicate no results
}
The above has no issue until i need to sort the list (ascending or descending) by the marks. I tried the following:
$students = Student::with(['result:student_id,marks' => function ($query) {
$query->orderBy('marks','DESC');
}])->whereIn('student.id', $eligibleList)->get();
It throws me a "Call to undefined relationship" error. Is there anyway to sort from the query ? I avoid sorting the collection as it can get very slow for thousands of records. Somehow eloquent early loading encounter some error when sorting with non existence relationship.
you should use 'Subquery Ordering', ordering inside 'with' will not sort the overall result.
$students = Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->orderByDesc(Result::select('marks')->whereColumn('student_id','students.id'))
->get();
https://laravel.com/docs/7.x/eloquent#advanced-subqueries
if you use laravel 5 you have to use 'join':
Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->join('result','result.student_id','student.id')
->select('user.*,result.marks')->orderBy('result.marks')->get();
'join' use table name not the relation name, so please be careful about table name in previous 'join' and 'select' statements

How to dissociate elements from a HasMany relation?

There's the save and saveMany methods on the HasMany relation class, but where are the dissociate(Many)/detach(Many) methods? There's also no built-in way to get the inverse relationship method, so what's the best way to dissociate an array of id's/models from a HasMany relationship object.
Currently I'm using:
$hasMany = $parent->theRelationship(); // Get the relationship object.
$child = $hasMany->getRelated(); // Get an empty related model.
$key = $hasMany->getForeignKeyName(); // Get the name of the column on the child to set to NULL.
$child->findMany($IDs)->each(function($model) use ($key) {
$model->$key = NULL;
$model->save();
});
This could be alot shorter with something like:
$hasMany = $parent->theRelationship();
$hasMany->dissociate($IDs);
Bonus points if you have any official answers from Taylor as to why he hasn't implemented this, I've seen him close feature requests of this kind on GitHub.
I am not sure why there isn't a function, but to be more performant than your example, you could use the DB class like:
\DB::table('child_table')->where('parent_id', $parent->id)->update(['parent_id' => null]);
You could use detach like so;
$parent->theRelationship()->detach([1,2,3])
Where you pass an array of IDs.
From Laravel documentation:
"For convenience, attach and detach also accept arrays of IDs as input"
The performatic way (1 db update):
$partent->theRelationship()->update(['parent_id' => null]);
The readable way (multiple db updates):
$parent->theRelationship->each->parentRelationship()->dissociate();

Laravel Eloquent - sync() on a Morph relationship

It seems like there is no sync() function for morph tables on Laravel.
I have two tables avails and questions. questions is a morphMany table. I want to use the sync command, and this is what I did:
Avail::find($id)->questions()->sync($some_ids);
This gives me the following error:
Call to undefined method Illuminate\Database\Query\Builder::sync()
So is there a way to get sync to work or am I just not doing this right?
morphMany is a 1-to-many, not many-to-many relation, so there is no sync method.
Use saveMany/save instead, and associate for the other way around.
to mimic sync behaviour for this kind of relation you can do this:
$questionsOld = $avail->questions()->get();
$questionsOld->each(function ($question) {
// appropriate fields here:
// 1*
$question->FOREIGN_ID = null;
$question->FOREIGN_TYPE = null;
$question->save();
});
$questionsNew = Question::whereIn('id', $someIds)->get();
// *2
$avail->questions()->saveMany($questionsNew->getDictionary());
Now:
*1 you can't use dissociate and must explicitly set relation_id and relation_type to null, since morphTo doesn't override this method, so it wouldn't work as expected.
*2 getDictionary() returns simple array of models, instead of collection. It's required becase saveMany typehints its parameter as array.

Doctrine toarray does not convert relations

I followed doctrine documnetation to get started. Here is the documentation.
My code is
$User = Doctrine_Core::getTable("User")->find(1);
when I access relations by $User->Phonenumbers, it works. When I convert User object to array by using toArray() method, it does not convert relations to array. It simply display $User data.
Am I missing something?
By using the find method you've only retrieved the User data which is why the return of toArray is limited to that data. You need to specify the additional data to load, and the best place to do this is usually in the original query. From the example you linked to, add the select portion:
$q = Doctrine_Query::create()
->select('u.*, e.*, p.*') // Example only, select what you need, not *
->from('User u')
->leftJoin('u.Email e')
->leftJoin('u.Phonenumbers p')
->where('u.id = ?', 1);
Then when toArray'ing the results from that, you should see the associated email and phonenumber data as well.
I also noticed an anomaly with this where if you call the relationship first then call the ToArray, the relationship somehow gets included. what i mean is that, taking your own eg,
$User = Doctrine_Core::getTable("User")->find(1);
$num= $User->Phonenumbers->office; // assumed a field 'office' in your phone num table
$userArray = $user->toArray(true);
In the above case, $userArray somehow contains the whole relationship. if we remove the $num assignment it doesn't.
am guessing this is due to doctrine only fetching the one record first, and it's only when you try to access foreign key values that it fetches the other related tables

Resources