Fartuch2332
Fartuch2332

Reputation: 21

Display parent id with many-to-many relationship

I have a relationship many-to-many. I need to fetch all tags with parent id. For example it can look like this:

[
   { article_id: 1, id: 1, name: "tag 1" },
   { article_id: 1, id, 2, name: "tag 2" },
   { article_id: 2, id: 1, name: "tag 1" }
]

Look that tag 1 is two times for two articles. Propably I can make loop inside loop but it doesn't look proffesional. Could you show me the best solution of this problem?

@Edit: I created this code:

public function articles(){
    $article = [];
    $allArticles = Articles::all();
    foreach($allArticles as $key => $a){
        $obj = new \stdClass();
        foreach($a->tags as $t){
            $obj->article_id = $a->id;
            $obj->name = $t->name;
            array_push($article, $obj);
        }

    }


    return json_encode($article);
}

But it doesn't work like I want. It display only one tag for one article, not all. Any idea?

Upvotes: 1

Views: 121

Answers (2)

Donkarnash
Donkarnash

Reputation: 12835

Don't quite understand what you are trying to achieve via the code snippet you have provided. However, as per the opening line of your question if you want to retrieve all tags with associated articles' ids then you can try the below

$tags = Tag::with('articles')
    ->get()
    ->map(function($tag) {
        $tag->articleIds = $tag->articles->pluck('id');
        unset($tag->articles);
        return $tag;
    });

This will give you an output like

[
   { id: 1, name: "tag 1", articleIds: [1,2] },
   { id, 2, name: "tag 2", articleIds: [1] },
]

If you want it other way around i.e. get the article with associated tags

$articles = Article::with('tags:id,name')
    ->get()
    ->map(function($article) {
        $object = new \StdClass;
        $object->article_id = $article->id;
        $object->tags = $article->tags;
        return $object;
    });

Which will give you an output like

[
   { article_id: 1, tags: [{id: 1, name: "tag 1" }, {id, 2, name: "tag 2"}] },
   { article_id: 2, tags: [{id: 1, name: "tag 1" }]},
]

Upvotes: 1

M Khalid Junaid
M Khalid Junaid

Reputation: 64476

Another way is do a join query which will return multiple rows for each article based on no. of tags assigned

$articles = DB::table('articles as a')
            ->select(['a.id','a.name','t.name as tag'])
            ->join('article_tags as at', 'a.id', '=', 'at.article_id')
            ->join('tags as t', 't.id', '=', 'at.tag_id')
            ->get();

so this way you don't need extra loop and no extra queries(N+1) will be fired to fetch related tags.

As per docs Relationship Methods Vs. Dynamic Properties

Dynamic properties are "lazy loading", meaning they will only load their relationship data when you actually access them. Because of this, developers often use eager loading to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model's relations.

If you still want to go with loop approach so atleast use eager loading like

$allArticles = Articles::with('tags')->all();

But still it will fire more than one query.

Upvotes: 0

Related Questions