Search code examples
laraveleloquentmany-to-manyquerying

Laravel> Using Eloquent Querying many-to-many relations


I am trying to figure out how does the Eloquent works and the advantages over using plain sql queries (join A and B tables on columA.id = columnB.id...)

I have two models: channels and subcategories in a many-to-many relation

I created a channel_subcategory pivot table and addded the relation like this:

public function up()
{
    Schema::create('channel_subcategory', function (Blueprint $table) {
        $table->increments('id');
        $table->timestamp('created_at')->useCurrent();
        $table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
        $table->integer('channel_id')->unsigned()->nullable();
        $table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
        $table->integer('subcategory_id')->unsigned()->nullable();
        $table->foreign('subcategory_id')->references('id')->on('subcategories') ->onDelete('cascade');
    });
}


I want to get one channel given the slug property and the subcategories it belongs to. So I did this in the ChannelController.php

 public function show($slug)
{
    $channel = Channel::where('channels.slug', '=', $slug)->first();
    foreach ($channel as $subcategory) {
        echo $subcategory->title;
    }
}

I get the error:

ErrorException in ChannelController.php line 107: Trying to get property of non-object


All I want is to show the channel name and the categories it belongs to.

I have read lots of blogs, including the Laravel documentation and they always explain migrations, how to establish the relation in the model (belongstomany), and even how to save related data. But nowhere I found a single description (for dummies) that tell, well now lets get some data out of the two tables like:

  • get all the categories one channel belongs to.
    • get all the channels in one category
    • get all the channels that belongs to more than one category
    • get the category with more channels.

In other words, a plain explanation on how to do this.

EDIT Adding my Channel model

public function subcategory()
{
    return $this->belongsToMany('App\Subcategory')->withTimestamps();
}

Solution

  • You need to define the relationship in your models. In your case you should create a subcategories relationship in the Channel model.

    public function subcategories()
    {
        return $this->belongsToMany(Subcategory::class, 'channel_subcategory', 'channel_id', 'subcategory_id')->withTimestamps();
    }
    

    Change SubCategory to whatever you've named the model.

    Then in your controller you can access the relationship like this

    $channel = Channel::where('slug', $slug)->first();
    
    foreach ($channel->subcategories as $subcategory) {
        echo $subcategory->title;
    }
    

    Though the above code works, you should always eager load if you're going to need relationship data. This prevents the N+1 query issue.

    $channel = Channel::with('subcategories')->where('slug', $slug)->first();