Next element in array - php

I create such function for find next element from my array
protected function getProject($array, $slug, $next = 999)
{
foreach($array as $key => $project)
{
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
}
}
But in result i have error "Maximum function nesting level of '100' reached, aborting". I know that there is a getSlug() isset. Please, help me solve this problem

I think the best way it's create new function, witch look like this:
public function yourAction($array, $key)
{
foreach($array as $index => $project) {
if($index == $key) {
return $project->getProduct();
}
}
}
And then usa like
if($project->getProduct() == $slug) {
$nextProduct = $this->yourAction($array, $key + 1)
return $nextProject;
}

Increase the limit of xdebug in php.ini
For example, making nesting level limit to 300:
xdebug.max_nesting_level=300
You can also comment out this line in php.ini to remove limiting constrain:
";xdebug.max_nesting_level"

You have an infinite loop/infinite recursion going on here.
foreach($array as $key => $project)
{
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
// ...
}
If there are any $array elements that match the condition $project->getSlug() == $slug, it will call getNextProject() again, which will run this foreach again thus calling getNextProject() again... and so on.
You need to reorder/re-think your logic here. You can try to move the if($next != 999) { to the top of the foreach.
Try this:
protected function getNextProject($array, $slug, $next = 999)
{
foreach($array as $key => $project)
{
if($next != 999 && $next == $key) {
return $project->getSlug();
}
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
}
}
Or better yet, you can just do this with one loop and no recursion:
protected function getNextProject($array, $slug)
{
foreach($array as $key => $project)
{
if($project->getSlug() == $slug && isset($array[$key+1])){
return $array[$key+1]->getSlug();
}
}
}
Just find the 1st element that matches the $slug, then get the next one and return its slug.

Related

Why is my code running from bottom to top

I am trying to do a transaction in the db with Eloquent ORM following the instructions here: https://stackoverflow.com/a/15105781/5649969
I notice that the code throws a Throwable, so I put it in a try...catch block to go for an early return if there is an exception.
try {
DB::transaction(function () use ($user, $attributes) {
$validUserAttributes = $user->getFillable();
$updatable = [];
foreach ($attributes as $key => $value) {
if (in_array($key, $validUserAttributes)) {
$updatable[$key] = $value;
}
}
$user->update($updatable);
$validRoleAttributes = $user->role->getFillable();
$updatable = [];
foreach ($attributes as $key => $value) {
if (in_array($key, $validRoleAttributes)) {
$updatable[$key] = $value;
}
}
$user->role()->update($updatable);
});
} catch (Throwable $_) {
dd(1000);
return new UpdateUserResult(false, UpdateUserResult::UPDATE_FAILED);
}
dd(2000);
return new UpdateUserResult(true, UpdateUserResult::UPDATE_SUCCESS);
This is where my problem is, it seems that the early return does not work for whatever reason, when I remove the dd(2000), dd(1000) will run, why does the code seem like it is running from the bottom to the top?

Request too heavy

I try to retrieve for each region the number of cities with current events.
So I started doing this:
$regionCities = [];
foreach ($regions as $region) {
$regionCities[$region->getId()] = $cityRepository->getCitiesByRegion($region);
}
dump($regionCities);
$regionCitiesNumber = [];
foreach ($regionCities as $index => $region) {
foreach ($region as $city) {
$regionCitiesNumber[$index] = count($city->getCurrentEvents());
}
}
My dump returns me this:
dump
The problem is, it crashes my script, and I suddenly get a blank page when I dump regionCitiesNumber.
getCurrentEvents is a method of my City entity that will retrieve all current events.
public static function createCurrentEventsCriteria(): Criteria
{
return Criteria::create()
->where(Criteria::expr()->gte('endDate', new DateTime('00:00:00')))
->orderBy(['id' => 'DESC'])
;
}
public function getCurrentEvents(): Collection
{
$criteria = EventRepository::createCurrentEventsCriteria();
return $this->events->matching($criteria);
}
Use error logs or try using "Try Catch" wrapper
$regionCities = [];
foreach ($regions as $region) {
$regionCities[$region->getId()] = $cityRepository->getCitiesByRegion($region);
}
//dump($regionCities);
try {
$regionCitiesNumber = [];
foreach ($regionCities as $index => $region) {
foreach ($region as $city) {
$regionCitiesNumber[$index] = count($city->getCurrentEvents());
}
}
} catch (\Throwable $t) {
dd($t);
}
So I tried:
return $this->events->matching($criteria)->count();
And I dumped on regionCitiesNumber, it gave me a totally wrong result:
For example for the ARA region, I end up with 0 as a result while there are 1895 events distributed over all the departments ...
As a result, most regions result in 0, sometimes 1, sometimes 2 ...
In addition, it takes a long time to load my page, isn't there a softer solution?

how to update an array using a function, php

Hey I have return a function that takes some orders and subtracts them from an array of inventory...however when am running the function it is only updating the inventory within the function and the original inventory doesn't change..I know this is something to do with scope but I don't know how to do it exactly..
How can I do this ? can you help?
function updateInventory ($inv) {
foreach($_SESSION["inventory"] as $bookDetails) {
foreach($_POST['orders'] as $k => $v) {
if($bookDetails['title'] == $k && $v == "hardcover") {
$bookDetails['hc-quantity']=intval($bookDetails['hc-quatinty'])-1;
}
}
}
}
When you assign an array to a variable, it gets a copy of the array, so changes to that variable don't affect the original array. You can use a reference to prevent copying.
function updateInventory ($inv) {
foreach($_SESSION["inventory"] as &$bookDetails) {
foreach($_POST['orders'] as $k => $v) {
if($bookDetails['title'] == $k && $v == "hardcover") {
$bookDetails['hc-quantity']--;
}
}
}
}

Dealing with an array that may or may not contain a specific offset

I have this function below:
public function set_partial($array)
{
if (is_array($array)) {
foreach ($array as $each) {
self::$_partials[$each[0]] = array('view' => $each[1], 'data' => $each[2]);
}
}
}
In self::$_partials, 'data' isn't required. So how do I keep my code simple while allowing data to be null? Right now, if data isn't provided, then I get an offset error.
You can check to see if each[2] isset. If it is, then set the variable, otherwise make it null:
<?php
public function set_partial($array){
if(is_array($array)){
foreach ($array as $each) {
self::$_partials[$each[0]] = array('view' => $each[1], 'data' => (isset($each[2])?$each[2]:NULL));
}
}
}?>
If you want to avoid offset errors, you can do something like this:
public function set_partial($array)
{
if (is_array($array)) {
foreach ($array as $each) {
$view = !empty($each[1]) ? $each[1] : ''; // replace '' with whatever default value you want to use
$data = !empty($each[2]) ? $each[2] : '';
self::$_partials[$each[0]] = array('view' => $view, 'data' => $data);
}
}
}

PHP: Modifying array with unknown structure at runtime; what is the most elegant solution?

PROBLEM
I have a function that takes in a nested array where the structure and nesting of the array is unknown at run-time. All that is known is some of the target fieldnames and desired values of some of the leafs.
QUESTIONS
1) I am hoping to modify this unknown structure and still have the code be readable and easily understood by fellow programmers. What (if any) solution will allow me to do things like this in PHP?
// Pseudo-code for things I would like to be able to do
// this is kinda like the same thing as XPATH, but for native PHP array
// find *every* fname with value of "Brad" and change it to "Brian"
$mydata->find_all('*:fname')->where_value_eq('Brad')->set_equal_to('Brian');
// find *the first* fave_color and set it to "Green"
$mydata->find('*:fave_color')->get(0)->set_equal_to('Green');
2) If there is nothing out there that will let me do this, is there something, anything, that at least comes close to the spirit of what I am hoping to accomplish here?
SAMPLE ARRAY
$mydata = array(
'people' => array(
array('fname'=>'Alice'),
array('fname'=>'Brad'),
array('fname'=>'Chris'),
),
'animals' => array(
array('fname'=>'Dino'),
array('fname'=>'Lassie'),
array('fname'=>'Brad'),
),
'settings' => array(
'user_prefs'=>array(
'localhost'=>array(
'fave_color'=>'blue',
),
),
),
'places' => array(
array('state'=>'New york',
'cities'=>array(
'name'=>'Albany',
'name'=>'Buffalo',
'name'=>'Corning',
),
'state'=>'California',
'cities'=>array(
'name'=>'Anaheim',
'name'=>'Bakersfield',
'name'=>'Carlsbad',
),
),
),
);
Although I maintain that you should stick with explicit manipulation as in my previous answer, boredom and intrigue got the better of me ;)
It probably has holes (and clearly lacks docs) but if you insist on this route, it should get you started:
class Finder {
protected $data;
public function __construct(&$data) {
if (!is_array($data)) {
throw new InvalidArgumentException;
}
$this->data = &$data;
}
public function all() {
return $this->find();
}
public function find($expression = null) {
if (!isset($expression)) {
return new Results($this->data);
}
$results = array();
$this->_find(explode(':', $expression), $this->data, $results);
return new Results($results);
}
protected function _find($parts, &$data, &$results) {
if (!$parts) {
return;
}
$currentParts = $parts;
$search = array_shift($currentParts);
if ($wildcard = $search == '*') {
$search = array_shift($currentParts);
}
foreach ($data as $key => &$value) {
if ($key === $search) {
if ($currentParts) {
$this->_find($currentParts, $value, $results);
} else {
$results[] = &$value;
}
} else if ($wildcard && is_array($value)) {
$this->_find($parts, $value, $results);
}
}
}
}
class Results {
protected $data;
public function __construct(&$data) {
$this->data = $data;
}
public function get($index, $limit = 1) {
$this->data = array_slice($this->data, $index, $limit);
return $this;
}
public function set_equal_to($value) {
foreach ($this->data as &$datum) {
$datum = $value;
}
}
public function __call($method, $args) {
if (!preg_match('/^where_?(key|value)_?(eq|contains)$/i', $method, $m)) {
throw new BadFunctionCallException;
}
if (!isset($args[0])) {
throw new InvalidArgumentException;
}
$operand = $args[0];
$isKey = strtolower($m[1]) == 'key';
$method = array('Results', '_compare' . (strtolower($m[2]) == 'eq' ? 'EqualTo' : 'Contains'));
$ret = array();
foreach ($this->data as $key => &$datum) {
if (call_user_func($method, $isKey ? $key : $datum, $operand)) {
$ret[] = &$datum;
}
}
$this->data = $ret;
return $this;
}
protected function _compareEqualTo($value, $test) {
return $value == $test;
}
protected function _compareContains($value, $test) {
return strpos($value, $test) !== false;
}
}
$finder = new Finder($mydata);
$finder->find('*:fname')->where_value_eq('Brad')->set_equal_to('Brian');
$finder->find('*:fave_color')->get(0)->set_equal_to('Green');
$finder->find('places:*:cities:*:name')->where_value_contains('ba')->set_equal_to('Stackoton');
print_r($mydata);
There's certainly no native solution for this and the syntax is rather strange. If you want the code to "be readable and easily understood by fellow programmers" please stick to methods that we're used to working with ;)
foreach ($mydata as $type => &$data) {
foreach ($data as &$member) {
if (isset($member['fname']) && $member['fname'] == 'Brad') {
$member['fname'] = 'Brian';
}
}
}
It's admittedly more verbose, but there's much less chance of confusion.

Categories