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
Related
I have json column where i save my orders data in it, code below is sample of that data:
[{"id":27,"name":"new product","price":7246,"quantity":"1","attributes":[],"conditions":[]}]
with code above if i have such loop in my edit page i'm able to get orders info:
#foreach($order->product_data as $data)
{{ $data['quantity'] }}
#endforeach
until here everything is good, but the problem when comes that my order has attributes "attributes":[], then i get this error:
Invalid argument supplied for foreach()
and my data in json column is like:
"[{\"id\":29,\"name\":\"effewf\",\"price\":24524,\"quantity\":\"1\",\"attributes\":[{\"attr\":{\"label\":\"Red\",\"price\":\"5000.00\"}},{\"attr\":{\"label\":\"22\\\"\",\"price\":\"900000.00\"}}],\"conditions\":[]}]"
as you see this order has 2 attributes. (can be more or less).
additional
this is how i get those data in order model:
protected $casts = [
'product_data' => 'array',
];
any idea why i get that error and how i can get my attributes data no matter if my order has attribute or not?
UPDATE
if i open edit page of order without attribute and dd the result will be like:
array:6 [▼
"id" => 27
"name" => "new product"
"price" => 7246
"quantity" => "1"
"attributes" => []
"conditions" => []
]
if load the same on order with attributes get the error above.
You should only allow to print data if exists and having the format we are expecting ,please add necessary conditions to your code like below which may prevent from getting the errors
Assuming the array you have in post is data you need to print.
#if($order->product_data) // check product_data value or not , also add key exists if needed
#if(is_array($order->product_data)) // check for product_data is array or not
#foreach($order->product_data as $data)
{{ $data['quantity'] // if array }}
{{ $data->quantity // if object }}
#if($data->attributes)
#foreach($data->attributes as $attribute) // loop through attributes
{{ $attribute->nameOfTheProperty // if object }}
#endforeach
#endif
#endforeach
#endif
#endif
But better to handle the data in controller if possible
update:
you need to convert your data into array, as your attribute property will be blank array if not having value otherwise it will be stdObject.
so your code should be : ($order->product_data should be an array if not then convert it to an array)
#if($order->product_data)
#if(is_array($order->product_data)) // assuming array
#foreach($order->product_data as $data)
{{ $data['quantity'] // if array }}
#if(count($data['attributes']) > 0) // **checked for blank array**
#foreach($data->attributes as $attribute) // loop through attributes
{{ $attribute['attr']['label'] }}
{{ $attribute['attr']['price'] }}
#endforeach
#endif
#endforeach
#endif
#endif
Tried with your sample data, and that executed without any error.
$json = "[{\"id\":29,\"name\":\"effewf\",\"price\":24524,\"quantity\":\"1\",\"attributes\":[{\"attr\":{\"label\":\"Red\",\"price\":\"5000.00\"}},{\"attr\":{\"label\":\"22\\\"\",\"price\":\"900000.00\"}}],\"conditions\":[]}]";
$jsonAsArray = json_decode($json, true);
foreach ($jsonAsArray as $data) {
echo $data['quantity'] . "\n";
if (! empty($data['attributes']) ) {
if (is_array($data['attributes'])) {
foreach ($data['attributes'] as $attribute) {
echo $attribute['attr']['label'] . ' ' . $attribute['attr']['price'] . "\n";
}
}
}
}
Outputs:
1
Red 5000.00
22" 900000.00
In your case, the product_data is not cast as an array.
You could try Defining An Accessor in your Order model, and remove the cast property of product_data
public function getProductDataAttribute($value)
{
return json_decode($value, TRUE);
}
Or Manually decode the json in your loop
#foreach (json_decode($order->product_data, TRUE) as $data)
{{ $data['quantity'] }}
#if (! empty($data['attributes']))
#foreach ($data['attributes'] as $attribute)
{{ $attribute['attr']['label'] }} {{ $attribute['attr']['price'] }}
#endforeach
#endif
#endforeach
This is how I did mine (category is a json column in the DB):
<div class="category">
<ul class="list-group">
#foreach ( json_decode( $code->category ) as $category)
<li class="list-group-item d-flex justify-content-between align-items-center">
<span class="badge badge-secondary badge-pill">{{$category}}</span>
</li>
#endforeach
</ul>
</div>
You just have to json_decode your json column to get an array and iterate over it. that's it, cheers.
I have 5 columns in my database. Here are the names of the columns:
promo1
promo2
promo3
promo4
promo5
I would like to combine these columns to be able to make a foreach in my view.
I tried this:
$promos = Building::select('promo1', 'promo2', 'promo3', 'promo4', 'promo5')->where('id', $card->id)->get()->toArray();
and in my view :
#foreach($promos as $promo)
<span>{{ $promo['promo1'] }}</span>
#endforeach
But I have only one result for my foreach. Instead of 5. And I can only select one by one ($ promo ['promo1'], $ promo ['promo2']) so doing a foreach would not help
Is there a way to do that? thank you very much
When you're using get() method, you're getting a collection. Use the find() method to get an object instead of collection:
$promos = Building::select('promo1', 'promo2', 'promo3', 'promo4', 'promo5')->find($card->id)->toArray();
Then you'll be able to iterate over promos:
#foreach ($promos as $promo)
<span>{{ $promo }}</span>
#endforeach
First get the building object
$building = Building::select('promo1', 'promo2', 'promo3', 'promo4', 'promo5')->findOrFail($card->id);
then loop through the promos
#foreach ($building->getAttributes() as $key => $value)
<span>{{ $key }} : {{ $value }}</span>
#endforeach
note: using the $key here is optional
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);
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
In laravel, is there some way of nesting related resources in a form?
Say I have this:
class Person extends Eloquent {
public function addresses() {
return $this->hasMany("Address");
}
}
class Address extends Eloquent {
public function person() {
return $this->belongsTo("Person");
}
}
and I want a Person form to collect information about that Person's Addresses. Does laravel facilitate this in a way that is equivalent to Rails' accepts_nested_attributes_for :address and fields_for :address?
I'd just like something simple where I can include the Address fields with the results of the Person form, since the Address doesn't really exist apart from the Person. Does this make sense?
== EDIT ==
This is hypothetical code
What I'm looking for is something that would resemble this:
{{ Form::model(new Person, array("action" => "admin\PersonController#store", "method" => "POST")) }}
{{ Form::text("name", array(...)) // <input name='person[name]' ... /> }}
{{ Form::email("email", array(...)) // <input name='person[email]' ... /> }}
{{ Form::fields_for("addresses"/* Would be name of relation */) }}
{{ Form::text("street_address") // <input name='person[addresses][][street_address]' ... /> }}
{{ Form::close_fields() }}
{{ Form::close() }}
You are on the right track with the input names.
Form
// Form open, Person fields, etc...
<h2>Addresses</h2>
#foreach ($addresses as $address)
<fieldset>
{{ Input::text('addresses['.$address->id.'][address_1]', $address->address_1) }}
{{ Input::text('addresses['.$address->id.'][address_1]', $address->address_2) }}
{{ Input::text('addresses['.$address->id.'][city]', $address->city) }}
{{ Input::text('addresses['.$address->id.'][state]', $address->state) }}
{{ Input::text('addresses['.$address->id.'][zip]', $address->zip) }}
</fieldset>
#endforeach
// Form Close
If you want to add addresses you'll need to generate some random key to use instead of the address id. This will keep the fields grouped.
Controller Logic
This is how I would handle input, using 'fillable' to filter the data going into the models.
// Get the Person model, fill, save, etc...
$addressIds = array();
foreach (Input::get('addresses', array()) as $id => $addressData)
{
$address = Address::find($id) ?: new Address;
$address->fill($addressData);
$address->save();
$addressIds[] = $address->id;
}
$changes = $person->addresses()->sync($addressIds);
// Delete the unused addresses
foreach ($changes['detached'] as $detachedAddressId)
{
$address = Address::find($detachedAddressId);
if (!empty($address)) $address->delete();
}
Now you can use the package "modelform" to create a form for various models or even a set of forms for relations.
https://github.com/andersondanilo/modelform
You should manage all of this from your controller. Collect the data from the form and use them as you please:
// Controller Code
$person = new Person(array(
'field_1' => Input::get('field_1'),
'field_2' => Input::get('field_2'),
.............
));
$person->save();
$address = new Address(array(
'field_x' => Input::get('field_x'),
'field_y' => Input::get('field_y'),
.............
));
$person->addresses()->save($address);
See it in the docs