I realized something I can't figure out by myself.
I get a different result when I loop through first in a Controller and pass then the result to my view. Versus loop straight in my view.
For instance:
I have this in my Controller:
public function index()
{
$subscribers = Subscriber::where('user_id', Auth::user()->id)->orderBy('created_at','asc')->get();
foreach ($subscribers as $key => $subscriber) {
$var = $subscriber->name;
}
return view('backend.newsletter.contacts.index')->withSubscribers($subscribers)
->withVar($var);
}
by using {{$var}} in my view I get only "John" as a result.
But when I use the foreach loop in my view instead of in the Controller:
#foreach($subscribers as $key => $subscriber)
{{$subscriber->name}}
#endforeach
I get two results, "John" and "Dan". This makes totaly sense as I have two entries in my DB.
So how does it come that I get two different results here ?
When you're doing this:
foreach ($subscribers as $key => $subscriber) {
$var = $subscriber->name;
}
With each iteration you overwrite $var so you always get the last value. For example, if you have ['Dan', 'John, 'Alice', 'Alan'], you'll get Alan.
Good practice is too pass data to a view and iterate it with #foreach.
Because only the last loop iteration is stored in $var.
You need to
$var = [];
foreach ($subscribers as $key => $subscriber) {
$var[] = $subscriber->name;
}
You are overriding $var in the loop.
Try this:
$var = [];
foreach ($subscribers as $key => $subscriber) {
$var[] = $subscriber->name; // Appends name to array
}
Related
controller:
$sold_fruits = [];
foreach ($clients as $client) {
$sold_fruits[] = $client->bought_fruits;
}
$supermarkets = [];
foreach ($sold_fruits as $sold_fruit) {
// HERE: I want to stop using this [0]
$supermarkets[] = $sold_fruit[0]->supermarket;
}
client model:
public function bought_fruits()
{
return $this->hasMany(BoughtFruits::class);
}
sold fruits model:
public function supermarket()
{
return $this->belongsTo(Supermarket::class);
}
In the first loop Im getting something like this:
[[obj1],[obj2]]. Thats why I have to use that [0] in there!
Is there any good way to stop using that [0]??
You can achieve your goal easily by using Laravel collection functions.
Use flatMap() when where there is an array of items in a property.
Use map() when there is only one item in a property.
In your case
There are many bought_fruits for a client, so use flatMap()
There is one supermarket for a bought_fruit, so use map()
$superMarkets = collect($clients)
->flatMap->bought_fruits
->map->supermarket;
You try to put index number in the array like these below.
$sold_fruits = [];
foreach($clients as $key => $value){
$sold_fruits[$key] = $value;
}
$supermarkets = [];
foreach($sold_fruits as $key => $value){
$supermarkets[$key] = $value;
}
NOTE: This is just to answer the question, scroll down more to see the other approach.
Note: obviously, this can "easily" be done with multiple nested foreach() loops, however I am looking for a way that is less expensive (and, if possible, also less spagetti).
Given a data array of objects like this:
$data (array)
[] (obj)
->id
->name
->a_one
->a_two
->b_three
->b_four
->c_five
[] (obj)
...
and this array of matching prefixes:
$prefixes (array)
[] (str) "a_"
[] (str) "b_"
[] (str) "c_"
how can I find (and unset) all properties of all objects in $data that begin with one of the defined $prefixes, without using multiple foreach() loops?
Solution using foreach():
foreach($data as $datakey => $obj) {
foreach($prefixes as $prefix) {
foreach($obj as $property => $value) {
if(strpos($property, $prefix) === 0) {
unset($data[$datakey]->{$property});
}
}
}
}
Is there a better way?
Construct a regexp pattern that matches all of the prefixes, and check each key against the regexp:
$pattern = "^(" . join("|", $prefixes) . ")";
foreach($data as $datakey => $obj) {
foreach($data as $datakey => $obj) {
foreach($obj as $property => $value) {
if (preg_match($pattern, $property))
...
}
}
This still requires two loops, but there is no redundancy; each object in your data is examined only once. If you were to find a way to loop over all the objects' contents without two PHP loops, it will still take just as many steps. The code in your question looped over each object multiple times (once for each prefix), which is indeed redundant.
Not sure whether you'll prefer this or not but;
$prefixes = ['a_', 'b_'];
$filtered = array_map(function($object) use ($prefixes) {
return unsetPrefixedProperties($object, $prefixes);
}, $data);
function unsetPrefixedProperties($object, $prefixes) {
$regex = '/('. implode('|', $prefixes) .').+/';
foreach ($object as $key => $value) {
if (preg_match($regex, $key)) {
unset($object->{$key});
}
}
return $object;
}
Similar to alexis basically using regex to match, removing the need to loop through your prefixes. Utilising array map to form a new array of filtered objects just because in my personal opinion it seems cleaner and finally extracted the other loop into its own function just to improve readability.
Still not perfect, but it's a start.
Edit: Sandbox Example
$matchedKeys = [];
foreach ($data as $key => $value) {
foreach ($prefixes as $val) {
$length = strlen($val);
if (substr($key, 0, $length) === $val) {
$matchedKeys[] = $key;
}
}
}
The $matchedKeys array will hold the matching keys from object $data.
How can I retrieve a data from my database in the controller, and assign it to a variable, that I will send to the view?
$data['schedule'] = $this->SectionsModel->getschedule($section);
I want the
$schedule['teacher']
(as equivalent to a view) data from the statement above to be passed to a variable, but how?
I tried this one but it shows an error
$data['schedule'] = $this->SectionsModel->getschedule($section);
foreach ($data['schedule'] as $key => $value){
echo $value['teacher'];
}
may be you can use result_array() in return value of your model.
Check your getschedule() method in your SectionModel if it's using the function result() instead of result_array().
The difference between the two methods is that result() will return object while result_array() will return array.
In you case,
$data['schedule'] = $this->SectionsModel->getschedule($section);
foreach ($data['schedule'] as $key => $value){
echo $value['teacher'];
}
Maybe $data['schedule'] is an object.You must use,
$data['schedule'] = $this->SectionsModel->getschedule($section);
foreach ($data['schedule'] as $key => $value){
echo $value->teacher;
}
If you will use result_array() method in your getschedule() in SectionModel to return result, do like this,
$data['schedule'] = $this->SectionsModel->getschedule($section);
foreach ($data['schedule'] as $key => $value){
echo $value['teacher'];
}
Hope this will help.
I have a Category array that I'm looping through and counting the subcategories that have their Category ID as the ID of the current item in the loop. It is retrieving the county just fine, uynfportunately when adding to the array it adds the value of zero. When echoed out, the values are as they should be, its only when inserting into the array the value becomes a zero.
This is the code I'm working with in PHP. I am using the Laravel framework.
public function index()
{
$categories = $this->categories->get()->toArray();
$categories_array = array();
$stats = array();
// dd($categories);
foreach ($categories as $key => $value)
{
$subcats_number = sub_categories::whereCategory_id($value['id'])->count();
$stats = array_add($stats, 'subcategories', $subcats_number);
echo $subcats_number.'<br/>';
$listings = classifieds::whereCategory_id($value['id'])->count();
$stats = array_add($stats, 'listings', $listings);
$categories_array = array_add($categories_array, $value['name'], $stats);
}
dd($categories_array);
// return view('admin.categories', compact('categories_array'));
}
The result of my dd:
Dump Result
I am not sure that array_add() helper is appropriate for your function here. As the doc says "The array_add function adds a given key / value pair to the array if the given key doesn't already exist in the array", and I am not sure that this is the proper behavior you need.
I would rather use a simple array_push() here:
public function index()
{
$categories = $this->categories->get()->toArray();
$categories_array = array();
foreach ($categories as $key => $value)
{
$subcats_number = sub_categories::whereCategory_id($value['id'])->count();
$listings = classifieds::whereCategory_id($value['id'])->count();
$stats = array('subcategories' => $subcats_number,'listings' => $listings);
array_push($categories_array,$value['name'] => $stats);
}
dd($categories_array);
}
Otherwise, you should also declare the $stats array into your loop if you choose to keep the array_add() function...
The end result I'm aiming for (if it's possible):
To iterate thru all $users and their $properties, grab the property address and then iterate thru $complaints (complaint belong to property) and create <table> for each property with it's complaints, it is possible that a property will not have complaints so need to check for that too.
I did try this code and managed to put all the $data in an array but first I can not iterate it in the email view and second if i'll have 1000 properties and each property will have 30 complaints... you get the idea... no good...
$users = User::all()->toArray();
foreach ($users as $user)
{
$report = [];
$properties = Property::where('user_id', $user['id'])->get()->toArray();
array_set($report, 'user', $user);
foreach ($properties as $property)
{
array_set($report, 'properties', $property);
$complaints = Complaint::where('property_id', $property['id'])->where('status', 'ACT')->get()->toArray();
foreach ($complaints as $complaint)
{
array_set($report, 'complaints', $complaint);
}
}
Mail::queue('emails.reports.weekly', $report, function($message) {
$message->to($report['user']['email'])->subject('Weekly Report');
});
}