Detach on eloquent collection not working - laravel

In a Post model I have:
public function tags()
{
return $this->morphToMany(\App\Models\Tag::class, 'taggable');
}
But when I then do:
$posts = Post::all();
$posts->tags()->detach();
I get
Method Illuminate\Database\Eloquent\Collection::inputs does not exist.

Based on your code, it seems like you're trying to remove all the tags from all the existing posts in your application.
If this is what you're trying to do, then follow #A.A Noman comment: you should detach them by iterating the collection, one by one.
Another option is to just clear the intermediate table containing the relations.
If what you're trying to do here is detaching all the tags from a single Post, you can search the post and then detach all the tags:
$post = Post::find($id);
$post->tags()->detach();
UPDATE
To iterate the collection and remove all the Tags from all the Posts:
$posts = Post::all();
foreach ($posts as $post) {
$post->tags()->detach();
}

Use Laravel's with()
https://laravel.com/docs/8.x/eloquent-relationships
To bring the tags related to this collection
Example:
$posts = Post::with('tags');
$posts->tags()->detach();
Likewise, if you want to remove a particular entity relationship from the pivot table, you can use the detach method.
For example, if you want to remove a certain author from a book, you can do so.
$book->authors()->detach($authorId);
Or you can pass multiple IDs as an array.
$book->authors()->detach([4, 5, 8]);
Or, if you want to remove all authors from a book, use detach without passing any arguments.
$book->authors()->detach();

Related

Laravel eloquent "with" the easy way

I have posted a related question a few hours ago but I can not understand why Laravel eloquent is so complicated... once again I read many posts on the subject and not one gave me the solution to a very simple request.
Here is the simple example where a post belongs to one article and one user only. So the relations are defined as below in the Post model:
public function article()
{
return $this->belongsTo(Article::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
I am quite new to Laravel eloquent... so I simply want to display a list of all the posts with their id, comment and related user name and article title as per relation defined in the model above. I believe that is quite simple and I just learned the "with" today.
So in the PostController, I have:
$posts = Post::orderBy($sortField,$sortOrder)
->with(['article','user'])
->get();
This gives me a collection with all the posts.
First question, do I have to manipulate the 2 related fields in such a way that they become easily accessible in the list view below? Or can I avoid this in controller in anyway by putting it in the query?
foreach ($posts as $post) {
$post->article = $post->article()->pluck('article');
$post->user = $post->user()->pluck('name');
}
In the above, the field $post->article lists all the article titles in an array (article field).
If I add [0] at the end, I get the first element of the array.
If I use [post->article_id] I get the wrong related title.
So nothing works the way I want... why is it so complicated???
Ultimately, here is what I need to do in the list view.
#foreach ($posts as $post)
<p>{{ $post->id }} : {{ $post->comment }} by {{ $post->name }} from article {{ $post->article }}</p>
#endforeach
By doing the query in query builder instead, I get what I want immediately... so how to do the same in eloquent?
$users = DB::table('posts')
->join('articles', 'articles.id', '=', 'posts.user_id')
->join('users', 'users.id', '=', 'posts.user_id')
->select('users.*', 'articles.article', 'users.name')
->get();
It is not clear what is making you misunderstand it, so here is an explanation.
First of all, you have two belongsTo relations user & article in your post model.
When you run this
$posts = Post::orderBy($sortField,$sortOrder)
->with(['article','user'])
->get();
You get a collection of Posts and with the article and the user in the attributes $post->article & $post->user respectively. That queryBuild will launch 3 queries, one for the posts, one for the articles and one final for the users.
Now, in your blade, you want to access the attribute article in your Article model wich is inside the Post model so you do it like this:
#foreach ($posts as $post)
.... from article {{ $post->article->article }}</p>
#endforeach
Notes: When you call $post->article() you get a query builder, so you follow it with a
first() => instance of a model / null
get() => Collection of instance of a model or empty collection
pluck() => array of the attribute plucked
value() => the value of the first element as if you used pluck
... (there are other ways to get results)
When you call $post->article without parenthesis, it will run :
first() if the relation is a belongsTo
get() if the relation is belongsToMany or HasMany or....
OR
It will give you the attribute if already fetched (either a collection or a model instance).
So, since you used with('article') in the first query (already fetched), $post->article will return an instance of the model Article and no new query will be run.

Combine collection and relation Eloquent

Is there a way to integrate the relation columns into the parent collection?
$r = User::with('contactDetails')->get();
Then be able to do merge them into a single object without having to do $r->user[0]->contactDetails->phonenumber but instead just $r->user[0]->phonenumber
You may use transform to iterate the collection and map on the existing object. It will be something like this;
User::with('contactDetails')
->get()
->transform(function (User $user) {
$user->phonenumber = $user->contactDetails->phonenumber;
return $user;
});
Another and more performant way could be using join since it is 1-to-1 relationship. Then you may put phonenumber into your select statement.

collections and eloquent results

I have a set of row returned from a function and stored in a variable called $topics. Each topic contains a set of articles. so for the variable $topics if we do $topics[0]->articles we get several articles, and if we do $topics[1]->articles we get another set of articles. What i want to do is to put all these articles in one variable lets call it $articles so that I can do operations such as
$articles->where('author', 'Jone Doe')->get();
I tried to do this:
$articles = new collection();
foreach( $topics as $topic) {
foreach( $topic->articles as $article)
$articles->push($article);
But this unfortunately destroys the actual structure of the articles. like I can't do $articles->translations where i use a translation package. but i can do $topics[0]->articles->translations. basically i want to get all the articles in one collection as if they were returned from doing something like
$articles = Article::all()->get();
Thanks
Just use the pluck method on your collection
$articles = $topics->pluck('articles');
And if you wish a 1-dimension articles collection
$articles = $topics->flatMap(function ($topic) {
return $topic->articles;
});

Database relation with Laravel Eloquent ORM

I'm new to Laravel and I'm stuck. This is what I am struggling with:
$questions = Question::find($id)->quiz(); // this code retrieves data from
// the table using the primary key
// in the table. The is a parameter
// that is passed via get.
This is what I have right now:
$questions = Question::where('quiz_id', '=', $id)->quiz();
This is the error I get:
Call to undefined method Illuminate\Database\Query\Builder::quiz()
What I want to do:
I want to run a query to get data from my database table using the foreign key in the table not the primary key, I also want to be able to use relations with this as seen from what I tried to do above.
Edit: Added the Question Model
<?php
class Question extends Eloquent{
protected $table = 'quiz_questions';
public function quiz()
{
return $this->belongsTo('Quiz');
}
}
Calling the quiz() function from Question::find($id)->quiz() will return a Query Builder instance allowing you to query the parent of the Question, its not going to return any data at that point until you call ->get() or another method that actually executes the query.
If you're wanting to return all the questions belonging to a certain quiz then you can do it like this.
$questions = Question::where('quiz_id', $id)->get();
This will return an Eloquent\Collection of the results for all questions with a quiz_id that is equal to $id.
If you've setup the relations between the Quiz and Questions then you can also do this using the Laravel relations.
$quiz = Quiz::findOrFail($id);
foreach($quiz->questions as $question)
{
// Do stuff with $question
}
Laravel will automagically pull Questions from the database that belongTo the Quiz you've already got from the database, this is known as eager loading http://laravel.com/docs/4.2/eloquent#eager-loading
Wader is correct, just calling where() will not execute your query. You either call get() and get an iterable result or use first() if you only want one result.
$quiz = Question::where('quiz_id', '=', $id)->first()->quiz();

Laravel Create Collection of a certain Model and add Objects manually

I got a list of IDs and need to get a Collection (Let's say of the type "Book") and add Single Books to this Collection by Hand.
Example:
$ids = array(1,5,7,12);
$books = ???
foreach($ids as $id){
$books->add(Book::find($id)); }
Or is there a easier way to get what I want?
Thank you
You could write something like this I think:
$books = Book::whereIn('id', $ids)->get();
$books will be a Collection containing all your books.

Resources