Optimize accesor function laravel - php

I have accesor, this function return position order for records of my model:
public function getPositionAttribute()
{
$n = $this->count();
for ($i = 0; $i < $n; $i++) {
$s = $this->get(['id'])->toArray()[$i]['id'];
if ($s == $this->id) {
return $i + 1;
}
}
}
How I can optimize this code? I think, that he works very very slow, because I going through a lot of records. May be do this with map function? I tryied, but map return collection, but not a number. My code with map:
public function getPositionAttribute()
{
$this->get(['id'])->map(function($item, $key) {
if ($item->id == $this->id) {
return $key + 1;
}
});
}
Map function return: #[1,NULL], #[NULL,2] e.t.c.
How I can correctly do my accesor function?

If i understood what your code does correctly, I'd probably do it like this:
public function getPositionAttribute()
{
$collection = $this->all()->pluck('id');
$position = $collection->search($this->id);
return $position ? ++$position : 0;
// 0 means the id could not be found
// You could easily swap that out with null or false.
}

Could you use the count of records having an earlier assigned primary key? For example:
public function getPositionAttribute()
{
return static::query()->where($this->getKeyName(), '<=', $this->getKey())->count() + 1;
}

Please try this and let me know if it works...
public function getPositionAttribute()
{
foreach($this->all() as $key => $item) {
if($this->id == item) return $key;
};
}
remember the collection starts in 0 maybe you need to add 1 before return $key.

Related

PHP: Increment the variable each time you run the function

I have a function in my Helper Class that should increment the variable each time the function is called.
Here is my code:
<?php
class Helper
{
public static $count;
public static function voiceHelper($questionArray)
{
$count = self::$count;
// $count = 0;
if(count($questionArray) >= $count)
{
$count++;
return $count;
} else if($count > count($questionArray))
{
$count == 0;
return $count;
}
}
}
I expect that the count variable will increment each time the function is called but it still remains 1.
Try:
class Helper
{
public static $count;
public static function voiceHelper($questionArray)
{
// $count = 0;
if(count($questionArray) >= $count)
{
self::$count++;
return self::$count;
} else if($count > count($questionArray))
{
self::$count = 0;
return self::$count;
}
}
}
Looks like you are just incrementing the $count without adding it to the static count property. Therefore you will always get 1. Instead actually increment the static count property.
You have to use self::$count everywhere:
<?php
class Helper
{
public static $count;
public static function voiceHelper($questionArray)
{
if(count($questionArray) >= self::$count)
{
self::$count++;
return self::$count;
}
if(self::$count > count($questionArray))
{
self::$count = 0; // change == to = as it's assignment
return self::$count;
}
}
}
Output:- https://3v4l.org/EaEqA And https://3v4l.org/pto7m
Note:- You did increment in the $count without adding it to the static count property. That's why you always got 1.

Is it possible to make a unique value in PHP?

I'm making a array class and want a value to be able to be returned by a higher order function. The idea is that its a instance constant or method returned value such that I can skip the value in a map.
In other languages making an array or some compound value, like ['skip'] will make it pointer equal such that I can then use the operator for pointer equal and it will not be equal to other arrays with the exact same content, but my problem is that ['skip'] === ['skip'] is true so even with === the two values are the same.
Here is an example of usage of my code where I accedentally have the same value as I used to skip:
namespace Test;
use Common\Domain\Collection;
$arr = new Collection();
$arr[] = 1;
$arr[] = 2;
$arr[] = 3;
$arr[] = 4;
echo count($arr); // prints 4
$arr2 = $arr->map(function ($v) {
return $v % 2 == 0 ? Collection::SKIP : ["skip"];
});
echo count($arr2); // prints 0, but should be 2
Is there a way to get a unique value or work around this somehow?
Here is code that implements Collection:
namespace Common\Domain;;
class Collection implements \Iterator, \Countable, \ArrayAccess
{
const SKIP = ["skip"];
private $arr = [];
public function map(callable $fn, bool $keepKeys = false) :Collection
{
$arr = new static();
$nOrder = 0;
foreach($this->arr as $key => $value) {
$result = call_user_func($fn, $value, $key, $nOrder, $this);
if($result !== self::SKIP) {
if($keepKeys) {
$arr[$key] = $result;
} else {
$arr[] = $result;
}
}
}
return $arr;
}
// implementation of interfaces \Iterator, \Countable, \ArrayAccess
public function current()
{
return current($this->arr);
}
public function next()
{
next($this->arr);
}
public function key()
{
return key($this->arr);
}
public function valid()
{
return isset($this->arr[$this->key()]);
}
public function rewind()
{
reset($this->arr);
}
public function count()
{
return count($this->arr);
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->arr);
}
public function offsetGet($offset)
{
return $this->arr[$offset];
}
public function offsetSet($offset, $value)
{
$this->arr[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->arr[$offset]);
}
}
I guess you are looking for Java-type enumerations, which doesn't exist in PHP. My best guess on your problem would be to use an object instead of a constant, that you would instantiate statically for a convenient use. Then, in the loop of your map function, you check the value with an instanceof instead of the basic equality operator, against the class you defined.
So, here :
class UniqueValue
{
public static function get()
{
return new self();
}
}
Then :
$arr2 = $arr->map(function ($v) {
return $v % 2 == 0 ? UniqueValue::get() : ["skip"];
});
And inside your collection :
public function map(callable $fn, bool $keepKeys = false) :Collection
{
$arr = new static();
$nOrder = 0;
foreach($this->arr as $key => $value) {
$result = call_user_func($fn, $value, $key, $nOrder, $this);
if($result ! instanceof UniqueValue) {
if($keepKeys) {
$arr[$key] = $result;
} else {
$arr[] = $result;
}
}
}
return $arr;
}
This is the quickest approach I can think of. If your array contains data from "outside" I don't think it's possible in any way that it matches against a class check from your own code.
I would solve this by implementing another method for this. The method delete would map a function over the collection and remove any elements where the function returns false.
e.g.
class Collection
{
// ...
public function delete($func)
{
$result = new static();
foreach($this->arr as $item)
{
if($func($item) !== false) $result[] = $item;
}
}
}
// example
$arr = new Collection();
$arr[] = 1;
$arr[] = 2;
$arr[] = 3;
$arr[] = 4;
echo count($arr); // prints 4
$arr2 = $arr->delete(function ($v) {
return $v % 2 ? true : false;
});
var_dump($arr2); // prints [2, 4]

Laravel recursion - calling itself in a class

My current code:
public function countThreads() {
$count = $this->threads->count();
if ($this->hasSubforum()) {
foreach ($this->subforums as $subforum) {
$count += $this->countThreads($subforum);
}
}
return $count;
}
I am currently accessing the "thread" as $this inside my model. I need to pass in the $subforum to itself but how can I do that in a class?
In my controller, I'm simply doing:
$forum = Forum::where('id', $id)->first();
$forum->countThreads();
How can I do recursion with this? thanks!
You don't need to pass any arguments*, you can call the countThreads method on the subforum $subforum->countThreads()
public function countThreads()
{
$count = $this->threads->count();
if ($this->hasSubforum()) {
foreach ($this->subforums as $subforum) {
$count += $subforum->countThreads();
}
}
return $count;
}
If you really want to pass it in as an argument, the correct way would be to write it as a service outside of the model

_.findWhere() in PHP?

What is the php implementation of underscore's _.findWhere({key:'val'})?
This is how the method is documented by Underscorejs.org:
_.findWhere(list, properties)
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
I wrote these little functions. Might be handy.
function where($list, $props)
{
$result = array_filter(
$list,
function ($e) use ($props)
{
$count = 0;
foreach ($props as $key => $value)
{
if ($value == $e[$key])
{
$count += 1;
}
return $count == count($props);
}
}
);
return $result;
}
function findWhere($list, $props)
{
$result = where($list, $props);
return array_values($result)[0];
}
There is none. Underscore is a collection of functions written in JS. It has nothing to do with PHP.

sort associative array codeigniter php

Here is what I want to do:
$newArray = array();
foreach($student as $s){
$newArray[$s->id][$s->grade] = $s;
}
I want to sort the students by their grades (more of a group than a sort) but I just want the grades to be sorted not the id. I could have don't this:
$newArray[$s->id] = $s->grade
asort($newArray)
but I need the remaining data in $s. Also, there is huge chunk of data associated with each student which I want to maintain.
How can I achieve such a sorting?
Edit:
Sine you're working in a framework, best declare your sort callback as a member function (inside the same class as where you'll be needing it, of course):
private function sortCB(array $a, array $b)
{//the array type hinting in arguments is optional
$i = array_keys($a);//but highly recommended
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
//replace '>' with '<' if you want to sort descending
return (end($i) > end($j) ? 1 : -1);//this is ascending
}
Then, in the method where the actual sorting is needed:
uasort($theArray,array($this,'sortCB'));
For more examples, see the docs. I've added a full class example at the end of this (bulky) answer
I've tried this on writecodeonline, which isn't all too good at this kind of stuff, but this did work:
$foo = array_fill_keys(array('foo','bar','q','Bond'),array());
$i = '256';
foreach($foo as $k=>$v)
{
$foo[$k][$i] = $k;
$i = (string)((int)$i%2 === 0 ? ((int)$i/2)+1 : (int)$i*3);
}
function sortCB($a,$b)
{
$i = array_keys($a);
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
return (end($i) > end($j) ? 1 : -1);
}
uasort($foo,'sortCB');
var_dump($foo);
But since you're using a framework, you might do well declaring that function as a member function private function sortCB(array $a,array $b), and use it like so:
uasort($foo,array($this, 'sortCB'));
There might be some more info on how best to use this callback function in a class context here
Full example + usage (tested and working):
class test
{
public $foo = null;
public function __construct()
{
$this->foo = array_fill_keys(array('foo','bar','q','Bond'),array());
$i = '256';
foreach($this->foo as $k=>$v)
{
$this->foo[$k][$i] = $k;
$i = (string)((int)$i%2 === 0 ? ((int)$i/2)+1 : (int)$i*3);
}
}
private function sortCB($a,$b)
{
$i = array_keys($a);
$j = array_keys($b);
if (end($i) === end($j))
{
return 0;
}
return (end($i) > end($j) ? 1 : -1);
}
public function sortFoo()
{
uasort($this->foo,array($this,'sortCB'));
print_r($this->foo);
return $this->foo;
}
}
$bar = new test();
$arr = $bar->sortFoo();
You can do something like:
foreach($student as $s){
$newArray[$s->id] = $s;
}
usort($newArray, function ($a, $b) { return $a->grade - $b->grade; });
Edit
For later versions that don't support anonymous functions you can define comparison function first:
function sortByGrade($a, $b)
{
return $a->grade - $b->grade;
}
usort($newArray, 'sortByGrade');
But if you get this data from db it would be easier to order it in your sql query. If you use ORM you can use its associated method.

Categories