Lansana Camara
Lansana Camara

Reputation: 9873

Laravel 5: Article hasMany Image, Image belongsTo Article. Getting a collection back

When I create an article, I can attach one image to it, which acts as the thumbnail. Everything works properly in that the image gets uploaded to the img directory, the image path gets added to the images table, and the article_id in the images table relates to the id of the article being created.

In my RouteServiceProvider I have this:

public function boot(Router $router)
{
    $router->bind('blog', function($id)
    {
        return Article::with('images')->findOrFail($id);
    });

    $router->bind('tags', function($name)
    {
        return Tag::where('name', $name)->firstOrFail();
    });

    parent::boot($router);
}

and in my view I have:

@foreach ($articles as $article)
    <h2><a href="/blog/{{ $article->id }}">{{ $article->title }}</a></h2>

    <p>{{ $article->body }}</p>

    <small>Posted on {{ date('F d, Y', strtotime($article->created_at)) }} by {{ $article->user->name }}</small>

    <img src="{{ $article->images }}">
@endforeach

{{ $article->images }} returns a collection, for example:

[{"id":17,"path":"img\/image2.jpg.jpg","article_id":49,"created_at":"2015-10-25 01:57:49","updated_at":"2015-10-25 01:57:49"}]

and it basically repeats the above for each article image in the foreach statement, except the id, article_id, path, etc. all changes.

{{ $article->images->path }} returns an error "Trying to get property of non-object". How can I write the code in my routeserviceprovider so that it gets just one image instead of a collection, so I can then use {{ $article->images->path }} without errors?

Upvotes: 0

Views: 683

Answers (2)

user3451822
user3451822

Reputation:

I have never really done it the RouterServiceProvider way, not sure if the rules are the same like in the Controller way. Anyways, the "Trying to get property of non-object" error suggests that Laravel thinks images from $article->images is not an object, so, maybe, if it is decoded into an array, it'll then be possible to access the members of the array as objects. Perhaps the following will shed some light to the solution of your problem.

In Controller:

    return View::make('pages.blog')
       ->with('images', json_decode($article->images));

The following suggests how I came to this thinking.

Code #1:

<?php

$data_string = "[{\"id\":17,\"path\":\"img\/image2.jpg.jpg\",\"article_id\":49, \"created_at\":\"2015-10-25 01:57:49\",\"updated_at\":\"2015-10-25 01:57:49\"}, {\"id\":23,\"path\":\"img\/image23.jpg.jpg\",\"article_id\":67, \"created_at\":\"2015-10-25 03:43:11\",\"updated_at\":\"2015-10-25 03:43:11\"}, {\"id\":11,\"path\":\"img\/image11.jpg.jpg\",\"article_id\":44, \"created_at\":\"2015-10-25 10:57:49\",\"updated_at\":\"2015-10-25 10:57:49\"}]";

$articles = json_decode($data_string);
var_dump($articles);

Output:

array(3) {
   [0]=>
   object(stdClass)#1 (5) {
      ["id"]=> int(17)
      ["path"]=>string(18) "img/image2.jpg.jpg"
      ["article_id"]=>int(49)
      ["created_at"]=>string(19) "2015-10-25 01:57:49"
      ["updated_at"]=>string(19) "2015-10-25 01:57:49"
   }
   [1]=>
   object(stdClass)#2 (5) {
      ["id"]=>int(23)
      ["path"]=>string(19) "img/image23.jpg.jpg"
      ["article_id"]=>int(67)
      ["created_at"]=>string(19) "2015-10-25 03:43:11"
      ["updated_at"]=>string(19) "2015-10-25 03:43:11"
   }
   [2]=>
   object(stdClass)#3 (5) {
      ["id"]=>int(11)
      ["path"]=>string(19) "img/image11.jpg.jpg"
      ["article_id"]=>int(44)
      ["created_at"]=>string(19) "2015-10-25 10:57:49"
      ["updated_at"]=>string(19) "2015-10-25 10:57:49"
   }
}

Code #2:

foreach($articles as $article) {
    echo "article(" . $article->id . "): " . $article->path . "\n"; 
}

Output:

article(17): img/image2.jpg.jpg
article(23): img/image23.jpg.jpg
article(11): img/image11.jpg.jpg

Upvotes: 1

Oliver Maksimovic
Oliver Maksimovic

Reputation: 3262

If your articles have 1 single image each, then use hasOne(). If your articles can have more than 1 image, then you need to iterate over each image (collection) to get their path, or use ->first()... or write a custom method that will return 1 single image according to whatever criteria it has to meet.

Upvotes: 1

Related Questions