clkline's avatar

May be an easy one: Limit response on chain relationship call?

Yea there's probably a better way to word that title, but I can't think of it at the moment. Thus Google was of no help because it doesn't speak confused-mind.

I am working further into "Project Flyer" tutorial, and am trying to adapt it's lessons to this school project (two more weeks of mayhem!).

Scenario: We have a story table. We have a story_photos table (just like in the Project flyer tutorial). Our home page is pulling in the ten newest stories - the top two will be "featured" with it's photo shown.

You can see here the basic template: 104.236.229.176

I know there is a call something like "get with photos", but as we are using Dropzone to allow multiple file uploads, I only want to grab the latest photo (for now).

Right now we are populating our home page with: story::take(10)->get()

Can someone help me understand what the code would look like to say "get ten stories with the first photo for each"?

0 likes
14 replies
bobbybouwmann's avatar

If you have the relation setup you can do this

Story::with('photos')->take(10)->get();

I didn't watch the "Project Flyer" series yet, so I'm not sure what you already have in your code. You might want to paste here what you think is necessary for us to help you.

1 like
clkline's avatar

Thank you! That is the call I was trying to remember off the top of my head. That is the call we are currently using, but my understanding is that this call returns ALL photos with each story (story 1 has it's 2 photos, story 2 has it's 4 photos, etc). Is there a way to limit "with('photos')" to only pull the latest photo for each story instead of all photos in the relationship?

x2luilop's avatar

First approach: get the last item of the photos collection

    $stories = Story::with('photos')->take(10)->get();

    foreach($stories as $story) {
        echo $story->name, '<br>';
        echo $story->photos->last()->name, '<hr>';
    }

Second approach: Adding another field to your stories table called "default_photo" and set the name of the latests photo.

bobbybouwmann's avatar

Well you can pass a closure, something like this

$stories = Story::with(['photos' => function($query) {
    $query->orderBy('created_at', 'desc')->take(1);
}])->take(10)->get();

Note: I didn't tested this! Just to head you in the right direction!

Edit: I tested it, and it works fine ;)

clkline's avatar

@thomaskim - Thank you for the article! I skimmed it and will read it more fully when I am in "homework mode". That is the concept I am looking for in general, I believe.

@lopezchairez - This is what I thought we were going to have to do - I like the idea of a "default" photo, and I already have in my head how that might work. I will work with my fellow developers on this (and continue to look for any definitive answers here until we are done).

EDIT: @bobbybouwmann - We posted at the same time lol. I think that is what I am going to try to do first. Thank you! Direction was all I needed :D

x2luilop's avatar

@bobbybouwmann Your approach will only get one photo and assign it to the last Story

Your code generates the following queries

SELECT * FROM `stories` LIMIT 10
SELECT * FROM `photos` WHERE `photos`.`story_id` in ('1', '2', '3', '4', '5') ORDER BY `created_at` DESC LIMIT 1
bobbybouwmann's avatar

@lopezchairez You are correct!

@clkline What you need to do is add a new relation method to your model

public function lastPhoto()
{
    return $this->belongsTo(Story::class)->orderBy('created_at', 'desc')->limit(1);
}

Now you can do this

$stories = Story::with('lastPhoto')->take(10)->get();
x2luilop's avatar

@bobbybouwmann I assume lastPhoto goes in the Photo Model, when I run the code I get a BadMethodCallException.

I put the method inside the Story model like so

public function lastPhoto()
{
    return $this->hasMany(Photo::class)->orderBy('created_at', 'desc')->limit(1);
}

But ended up getting one photo assigned to the last Story like before

clkline's avatar

This made for a fun post-dinner read ROFLOL! Thank you! I will look at this article :)

Please or to participate in this conversation.