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

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.

Related

How can I use Advanced Custom Fields with timber to retrieve Array values?

I'm trying to retrieve the user's metadata by first calling a custom field (using advanced custom fields plugin), using array values, the problem is that I could do that using PHP, but I have to use Timber because of my theme and there's not much info out there teaching how to use Timber and ACF (using advanced custom fields), even the info available is confusing and poor. I'm using Wordpress with Gantry5 framework and Helium theme.
First I set the custom field in ACF as "relational > user", then I set the data format as User Array, save, then I open a post and setup the fields inside the post, by choosing the user of each field.
So let's say the field name is "post_autor" and I need to display it below the post, the only problem is that I need to retrieve its arrays, so this is what I've tried to find the array values:
{% set author = post.get_field("post_autor") %} then {{ dump() }}
That line of code goes here:
“themes/g5_helium/custom/views/partials/content-single.html.twig”, after this part:
{# Begin Page Content #}
{{ post.paged_content|raw }}
{{ function('wp_link_pages', {'before': '<div class="page-links" itemprop="pagination"><ul class="pagination-list">', 'after': '</ul></div>', 'link_before': '<span class="page-number page-numbers">', 'link_after': '</span>', 'echo': 0}) }}
{# End Page Content #}
And this is the result I'm getting:
then array(1) { [0]=> array(11) { ["ID"]=> int(1) ["user_firstname"]=> string(7) "John" ["user_lastname"]=> string(5) "Doe" ["nickname"]=> string(4) "john" ["user_nicename"]=> string(13) "johndoe" ["display_name"]=> string(13) "John Doe" ["user_email"]=> string(23) "contact#site.com" ["user_url"]=> string(23) "https://siteDOTcom" ["user_registered"]=> string(19) "2019-03-12 03:53:10" ["user_description"]=> string(0) "" ["user_avatar"]=> string(472) "John Doe" } }
Of course I changed some data before posting here, because it's personal data.
I believe that this tutorial https://www.advancedcustomfields.com/resources/querying-relationship-fields/ has something to do with what I'm trying to achieve, but I'm not sure.
Basically it's this:
Field name --
=> Array value 1
=> Array value 2
=> Array value 3
First I need to get the field, then retrieve its "sub" values and display them. I tried to do my best to explain this, if anyone else needs more info, just ask.
Thanks in advance.
To access elements from a 2d array you can do {{ author.user_firstname }} or if there were further level you can do {{ author.level1.level2 }}
FYI, you can still use php and then add the data to your twig files by extending the context.
For example in your single.php
// usual php stuff
$logic = some_logic_function();
// get context
$context = Timber::context();
// add to context
$context['logic'] = $logic;
// render view
Timber::render('single.twig', $context);
Then in your single.twig file you can access the data as such:
{{ logic }}
as indicated in your comment, acf fields can be accessed in much the same way as you'd access other meta properties.
if it's a repeater, it'll have multiple values, so you can loop over them (only use .meta for the 'parent' property)
code sample for template.twig:
{% if post.meta('partner_organisations')|length %}
{% for item in post.meta('partner_organisations') %}{% if item.status == "publish" %}
{{ item.name }}<br />{% endif %}
{% endfor %}
{% endif %}
here, first we make sure there's actually some values. as this is for Posts, they have a status which might be draft, so we check for a published status before displaying the linked name. if you wanted an unordered list, you'd add the <ul> tags inside the if
you can also build an array with the relevant references in your template.php before passing it over to twig.
other ways of interacting with arrays/lists in timber/twig:
{{ dump(post.meta('list_field')|first) }}
{{ post.meta('list_field')|join(', ') }}
{{ post.meta('list_field')|join(', ', ' and ') }}
documentation on nested repeaters and more is available here https://timber.github.io/docs/guides/acf-cookbook/#nested-repeater-fields
if you're looking for details specifically using a relationship field - the doc above also has info on that, as you;d want to make sure it's a Timber\Post so you can interact with its properties as usual:
{% for item in Post(post.relationship_field) %}
{{ item.title }}
{# Do something with item #}
{% endfor %}

Object variable don't display in a twig template

I find this strange behaviour when using Twig (v1.23.3)
In a templace I have an array of objects : I use the dump function to display it, like this :
<pre align="left" style="text-align: left; font-size: 80%">
{{ dump(current_balances) }}
</pre>
{% for key, val in current_balances %}
{{ val.name }} : {{ val.balance | number_format(2, '.', ' ') }} €<br>
{% else %}
Aucun solde trouvé.
{% endfor %}
The output of dump is as expected :
array(2) {
[0]=>
object(Record)#20 (3) {
["name"]=>
string(32) "Bank 1 - Account"
["balance"]=>
string(7) "2000.00"
}
[1]=>
object(Record)#21 (3) {
["name"]=>
string(32) "Bank 2 - Account"
["balance"]=>
string(8) "1000.00"
}
}
But the output of the for loop is not correct. {{ val.name }} is displayed, but {{ val.balance }} is empty.
I dit this kind of loops hundreds of time - this code is merely cut-and-paste from a template that works.
Event more curious is that, if I invert the order of the vars in the object (as is it matters), putting balance before name, then balance is displayed, and not name !
Don't really know where to look with this one. Of course, my template are rendered with controllers calling models, so this is only a little part of the code.
I think I found a bug, similar to Variables not replaced in Twig template but it's hard to tell.
That's not a bug ! I finally enable the cache in Twig and looked at the resulting code. Guess what ? Some non-printable char has find its way near the val.balance. A "cat -A" on the template file shows this :
{{ val.name }} : {{M-BM- val.balance }} M-bM-^BM-,<br>$
And result in this in the compiled template :
$this->getAttribute((isset($context[" val"]) ? $context[" val"] : null)
Noticed the space before val ?
Lost one hour with this one. Source problem is an obsolete frenck keymap in Debian....

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>{{ $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

Cannot loop through custom array

Using Laravel 4 and trying to create an array of objects without using eloquent.
That array is made this like:
$item_collection[] = [
'item_type' => $item->item_type->name,
];
This returns a long list of items in an array:
array(258) {
[0] array(1) {
["item_type"] "Deal Bag"
}
[1] array(1) {
["item_type"] "Car"
}
[2] array(1) {
["item_type"] "Deal Bag"
}
}
In my view I am trying to loop like normal:
#foreach ($items as $item)
<tr>
<td>{{ $item->item_type }}</td>
</tr>
#endforeach
But receiving "Trying to get property of non-object" for all the values.
Where am I going wrong either with making / looping this array?
Try using array notation:
{{ $item['item_type'] }}

Is it possible to store an array as flash data in Laravel?

I have been looking around for a way of doing this. I know it is possible to store an array in the session with the following: Session::push('user.teams', 'developers');
Is it possible to do the same but with flash data? Something like Session::flashpush('user.teams', array('developers', 'designers')); would be great.
The usecase for me at this moment is primarily the following:
Session::flash('flash_message', $validator->messages());
As far as I know, you can do it. I've checked this just in case:
Session::flash('test', array('test1', 'test2', 'test3'));
... After the request
dd(Session::get('test'));
// array(2) { [0]=> string(5) "test1" [1]=> string(5) "test2" [2]=> string(5) "test3" }
It works. Also you can serialize an array or object as Christopher Morrissey just commented
I use this:
Session::flash($key, array_merge((array)Session::get($key), array($value)));
I created this helper class:
<?php
class Flash {
public static function push($key, $value) {
$values = Session::get($key, []);
$values[] = $value;
Session::flash($key, $values);
}
}
It allows you push multiple items to the same key such that it always return an array when fetched.
Usage:
Flash::push('success','Feature saved');
Twig template (Blade shouldn't be too much different):
{% if session_has('success') %}
<div class="alert alert-block alert-success fade in">
<button class="close" data-dismiss="alert">×</button>
{% for msg in session_get('success') %}
<p><i class="fa-fw fa fa-check"></i> {{ msg }}</p>
{% endfor %}
</div>
{% endif %}
In your scenario, you would probably use it like this:
Flash::push('flash_message', 'user.teams');
Flash::push('flash_message', 'developers');
Laravel 8
This is the most efficient way I've found using the latest version of Laravel if you want to store info input by the user as an array and then pull it separately. I'm using for a Madlibs app.
$req->session()->put([
'adj1' => $req->input('adj1'),
'noun1' => $req->input('noun1'),
'place' => $req->input('place'),
]);
return $req->session()->flash('test',
array('adj1', 'noun1', 'place',
));
I hope this helps a future users.

Categories