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

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>{{ $article->title }}</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?

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.

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

Related

Laravel - blade check if object has relationship

In my controller I'm getting a complex query that filter data depending on input provided by the user:
if (conditionB) {
$query = Model::with('relationA', 'relationB');
} else {
$query = Model::with('relationA', 'relationC');
}
// checking filters and apply conditions...
$query = $query->get();
Now in my blade file I'm looping result and I want to display "eager load" relationship B or C:
#if (count($myObject->relationB)) {
<span>{{ $myObject->relationB->someField }}</span>
#else
<span>{{ $myObject->relationC->someField }}</span>
#endif
It works, but if my $myObject has relationC and not relationB, then
during the verification of the if condition the relationship is loaded.
How I can avoid loading that relationship??
when I do: {{ $myObject }}
It gives me:
// has relationB
"field1": value1,
"field2": value2,
....
"fieldx": valuex,
"relation_b":[
{
"relationBfield1": relationBvalue1,
"relationBfield2": relationBvalue2,
},
{ ... }
// doesn't have relationB
"field1": value1,
"field2": value2,
....
"fieldx": valuex
I tried access relation_b (note that json is changing name by replacing UpperCase for _<LowerCase>) but it returns NULL, but if I access relationB it shows data:
myObject->relation_b; // NULL
myObject->relation_b[0]; // NULL
myObject->relationB; // works, but if item has RelationC it will "eager load" relationship B just to check that if condition
I was trying different options, and I don't see where I make mistake, I also tried to create a public function in my model:
{{ $myObject->checkRelation() }}
public function checkRelation()
{
if (count($this->relationB) > 0) {
// code...
But I always end up with the same problem, when object item doesn't have relationB it will load it to check that if condition.
How can I avoid it? Or there is another way to check with relation has my object item without executing an additional query?
If you want to check if a relationship has been loaded you can use the relationLoaded() method on the model:
#if ($myObject->relationLoaded('relationB')) {
<span>{{ $myObject->relationB->someField }}</span>
#else
<span>{{ $myObject->relationC->someField }}</span>
#endif

Laravel VentureCraft/revisionable returned name record with databse

Hello in my project I using VentureCraft/revisionable https://github.com/VentureCraft/revisionable This is addition to recording the history of changes to records in databse. Everything works fine, but when I returned the history on page, the script returns the results in the form of id. This is my table "products" products_table and this is table "revisions" revisions_table This table is connected to the tables: articles, users and categories. Now the history change looks like this:
"User 1 change name article from 2 to 1"
I want to the history change looked like:
"User JACK change name article from SHOES to BALL"
I try using this link.
method identifiableName() with file Revisionable.php looks like this:
public function identifiableName()
{
return $this->getKey();
}
In model product.php I added:
public function identifiableName(){
return $this->article_name;
}
or this:
public function identifiableName(){
return $this->article()->article_name;
}
but it does not work.
OK I found solution to this problem:
Only just use:
whereId or find or faindOrFail
example:
article::whereId( $hist->old_value )->first()->article_name
or
article::find( $hist->old_value )->article_name
or
article::findOrFail( $hist->old_value )->article_name
Where article:name is name column in articles table.
you can use
$history->userResponsible()->first_name
like the documentation says
#foreach($resource->revisionHistory as $history)
#if($history->key == 'created_at' && !$history->old_value)
<li>{{ $history->userResponsible()->first_name }} created this resource at {{ $history->newValue() }}</li>
#else
<li>{{ $history->userResponsible()->first_name }} changed {{ $history->fieldName() }} from {{ $history->oldValue() }} to {{ $history->newValue() }}</li>
#endif
#endforeach
or if you want to use it as response api, you can manipulate the response to get their relations
$response = $question->revisionHistory->map(function($history) {
return ['history' => $history, 'users' => $history->userResponsible()];
});
return response()->json($response);

Laravel Blade returns a "Undefined property" on an object that has a defined property

I have a model with several fields, including two text fields that store JSON lists of dictionaries. One of them, that stores image data, is working fine; however a second one that stores a list of dicts with links returns a Undefined property: stdClass::$title when I try to access the property from a blade template.
All of the other properties (including the images JSON converted to an array of objects) are rendering fine if I remove the call to my links property.
I've tried to dd() the links property and it both shows that it's set, it's an array, and it's full of objects with both the properties (title, url) that I'm trying to access when it fails.
Once I try to actually access them, however, I get that Undefined property for the exact properties I'm trying to access.
Wondering if anyone has encountered anything like this? The really odd thing is that the images JSON data is rendering without a single problem. It's all tied together with Route Model binding, which is verified to be working.
Property getters in Eloquent Model
public function getLinksAttribute() {
if (!empty($this->attributes['links'])) {
return json_decode($this->attributes['links']);
}
}
public function getImagesAttribute() {
if (!empty($this->attributes['images'])) {
return json_decode($this->attributes['images']);
}
}
Section of blade template calling the link property, that fails
#if (is_array($artist->links))
<div class="links">
<h4>Links</h4>
<ul>
#foreach ($artist->links as $link)
{{ $link->title }}, {{ $link->url }}
#endforeach
</ul>
</div>
#endif
Section of blade template calling the images property, that succeeds
#if (is_array($artist->images))
<ul class="images">
#foreach ($artist->images as $image)
<li>{!! Html::image(Html::buildS3Url(array(
"basedir" => "artists", "id" => $artist->id, "prefix" => $image->prefix,
"extension" => $image->extension, "conversion" => "display")
), $artist->name) !!}</li>
#endforeach
</ul>
#endif
Json encoded data in database via tinker, and subsequent dd()
### links (doesn't work)
#tinker output
links: "[{"'title'":"test","'url'":"http:\/\/test.com"}]",,
# dd()
array:1 [▼
0 => {#308 ▼
+"'title'": "test"
+"'url'": "http://test.com"
}
]
### images (works)
# tinker output
images: "[{"prefix":1440693993,"extension":"png"},{"prefix":1440697822,"extension":"png"}]"
# dd()
array:2 [▼
0 => {#308 ▼
+"prefix": 1440693993
+"extension": "png"
}
1 => {#307 ▼
+"prefix": 1440697822
+"extension": "png"
}
]
"Undefined property: stdClass::$title"
seems like in one of Your links missing title property
You can check it by:
#foreach ($artist->links as $link)
<?php if(property_exists($link, "title")) : ?>
{{ $link->title }}
<?php else : ?>
NO TITLE [DEBUG: {{ dd($link) }}]
<?php endif; ?>, {{ $link->url }}
#endforeach
also I've found one thing:
links: "[{"'title'":"test","'url'":"http:\/\/test.com"}]",,
Your element field is ' title ' (with '), but it must be "title": "test"
You can fix it by removing single quotes in paramater names.

Laravel Passing Variable from Forms

I am brand new to Laravel, and following a super basic tutorial.
However the tutorial did not come with an edit record section, which I am attempting to extend myself.
Route:
Route::controller('admin/products', 'ProductsController');
Controller:
class ProductsController extends BaseController
{
public function getUpdate($id)
{
$product = Product::find($id);
if ($product) {
$product->title = Input::get('title');
$product->save();
return Redirect::to('admin/products/index')->with('message', 'Product Updated');
}
return Redirect::to('admin/products/index')->with('message', 'Invalid Product');
}
..ECT...
I realise the controller is requesting an ID to use, but I cannot figure out how to pass it a product ID when the form is posted/get.
Form:
{{Form::open(array("url"=>"admin/products/update",'method' => 'get', 'files'=>true))}}
<ul>
<li>
{{ Form::label('title', 'Title:') }}
{{ Form::text('title') }}
{{ Form::hidden('id', $product->id) }}
..ECT...
{{ Form::close() }}
my initial idea was to pass the product id within the form URL like:
{{Form::open(array("url"=>"admin/products/update/{{product->id}}", 'files'=>true))}}
But no luck with that either.
The error I get is:
Missing argument 1 for ProductsController::postUpdate()
Interestingly if I type directly into the URL:
http://localhost/laravel/public/admin/products/update/3
It works and the item with id 3 is altered fine.
So can anyone help and inform me how to pass the id with a form?
Thanks very much
The first Problem here ist the following:
{{Form::open(array("url"=>"admin/products/update/{{product->id}}", 'files'=>true))}}
the {{product->id}} is wrong in two ways:
it should be {{$product->id}}
BUT it wouldn't work anyway because the inner {{..}} inside of the {{Form::...}} won't be recognized since it is inside a string and therefore part of the string itself.
You either have to write it this way:
{{Form::open(array("url"=>"admin/products/update/".$product->id, 'files'=>true))}}
or you give your route a name in your routes.php file and do it this way:
{{Form::open(array('route' => array('route.name', $product->id, 'files'=>true)))}}
I prefer the second way.
You also might want to look into Form Model Bingin

Looping PHP Nested Arrays - Extract values into Blade Views (Laravel)

I know there are many questions on this topic, but none quite deal with this (as far as I could see).
I have a PHP array (which FYI, is returned via Guzzle response) in a Laravel Project.
The PHP array
$users = array(2) {
["error"]=>
bool(false)
["spirits"]=>
array(2) {
[0]=>
array(2) {
["id"]=>
string(1) "1"
["name"]=>
string(5) "Foo"
}
[1]=>
array(2) {
["id"]=>
string(1) "2"
["name"]=>
string(3) "Bar"
}
}
}
I simply want to extract the "id" and "name" keys below, to use in a view but I'm a little stumped. I've tried the suggestions below, but can't quite work it out.
How to Flatten a Multidimensional Array?
PHP foreach with Nested Array?
I've also looked into array_walk_recursive.
Any help would be awesome and appreciated! I want to be able to use these 2 keys in Laravel like so:
Controller
return View::make('users')->with('users',$users);
View
#foreach ($users as $key => $user)
{{ $user["id"] }}
{{ $user["name"] }}
#endforeach
You may try this:
#foreach ($users['spirits'] as $user)
{{ $user["id"] }}
{{ $user["name"] }}
#endforeach
It's better to check the returned result in your controller before you send it to the view using something like this so there will be no errors in your view:
$users = 'Get it from somewhere...';
if(!$users['error']) {
return View::make('users')->with('users', $users);
}
else {
// Show an error with a different view
}
in case your users are always stored in the spirits-key of your $users variable you simply could modify your #foreach-loop as follow:
#foreach ($users['spirits'] as $user)
{{ $user['id'] }}
{{ $user['name'] }}
#endforeach
Otherwise you could edit your return value from the controller. That means you simply could change the line:
return View::make('users')->with('users',$users);
to
return View::make('users')->with('users',$users['spirits']);
In this case you don't have access to your error-key.

Categories