Let's say I have a child of a parent collection and I want to know who the next sibling is. My parent collection is ordered differently than internal id so I can't use the method described here:
Laravel previous and next records
Which would work except I'm sorting by name and time, not internal id. I'm hoping that there's a way to just get the parent collection, find this child's position within it, and then look forward or back within that collection to get next or previous.
Edit:
So, I made this, which works, but seems clunky. Is there a more efficient way to do this?
public function next()
{
$previous = null;
foreach ($this->album->media as $media)
{
if(!empty($previous && $previous->id == $this->id))
{
// Yay! Our current record is the 'next' record.
return $media->id;
}
$previous = $media;
}
return null;
}
public function previous()
{
$previous = null;
foreach ($this->album->media as $media)
{
if(!empty($previous && $media->id == $this->id))
{
// Yay! Our previous record is the 'previous' record.
return $previous;
}
$previous = $media->id;
}
return null;
}
You should never load the entire table to loop through it to find the next/previous item, instead do it this way:
$next = $this->album->media()->orderBy('id')->where('id', '>', $this->id)->first();
Here is the simple line of code
// next
function next($product_id)
{
$next = Product::where('id', '>', $product_id)->first();
return $next;
}
// previous
function previous($product_id)
{
$previous = Product::where('id', '<', $product_id)->first();
return $previous;
}
This did the trick:
public function next()
{
$previous = null;
foreach ($this->album->media as $media)
{
if(!empty($previous && $previous->id == $this->id))
{
// Yay! Our current record is the 'next' record.
return $media->id;
}
$previous = $media;
}
return null;
}
public function previous()
{
$previous = null;
foreach ($this->album->media as $media)
{
if(!empty($previous && $media->id == $this->id))
{
// Yay! Our previous record is the 'previous' record.
return $previous;
}
$previous = $media->id;
}
return null;
}
Related
I have a binary tree and node class that can create nodes and then recursively traverse the root for pre, post and in-order node orders. This code works when in JS, but for some reason infinitely loops with a warning of "Cannot use '$this' in non-object context." when returning $this in the addSide() function. What is causing this infinite loop, and how can I fix it?
<?php
class Node {
public $value;
public $right = null;
public $left = null;
function __constructor($value) {
$this->value = $value;
}
}
class BinaryTree {
public $root;
function __constructor() {}
function create($value) {
$newNode = new Node($value);
if (!$this->root) {
$this->root = $newNode;
return $this; //no warning
}
$current = $this->root;
function addSide($side, $current, $newNode) {
if (!$current->$side) {
$current->$side = $newNode;
return $this; //Warning: "Cannot use '$this' in non-object context."
}
$current = $current->$side;
};
while (true) {
if ($value === $current->value) return $this;
if ($value < $current->value) addSide("left", $current, $newNode);
else addSide("right", $current, $newNode);
}
}
function preOrder() {
$visited = [];
$current = $this->root;
function traversePreOrder($node) {
array_push($visited, $node->value);
if ($node->left) traversePreOrder($node->left);
if ($node->right) traversePreOrder($node->right);
};
traversePreOrder($current);
return $visited;
}
function postOrder() {
$visited = [];
$current = $this->root;
function traversePostOrder($node) {
if ($node->left) traversePostOrder($node->left);
if ($node->right) traversePostOrder($node->right);
array_push($visited, $node->value);
};
traversePostOrder($current);
return $visited;
}
function inOrder() {
$visited = [];
$current = $this->root;
function traverseInOrder($node) {
if ($node->left) traverseInOrder($node->left);
array_push($visited, $node->value);
if ($node->right) traverseInOrder($node->right);
};
traverseInOrder($current);
return $visited;
}
}
$tree = new BinaryTree();
$tree->create(50);
$tree->create(30);
$tree->create(45);
$tree->create(12);
$tree->create(29);
echo("inOrder: ". $tree->inOrder());
echo("preOrder: ". $tree->preOrder());
echo("postOrder: ". $tree->postOrder());
Since you don't seem to be from a PHP background, here are some of the things to note down:
It is __construct() and not __constructor(). This served to be a major problem in the code during value comparisons.
No need to create functions inside functions. This can lead to cannot redeclare function issues when a method is called twice.
When calling a method from another method inside a class, $this-> is necessary unless the function being called is an inbuilt function in PHP or at least available during code execution.
You seem to be creating a Binary Search Tree instead of just a Binary Tree.
Pass $visited by reference when collecting values during traversal.
You can't print arrays using echo. Use print_r() or use implode() to convert the array to string using a delimiter(say ,) and then print it using echo.
In create(), you sometimes return a node and sometimes $this. Both are not the same. Former one is an object of the Node class and the latter one is the object of the BinaryTree class.
In create() method, you simply need to traverse left or right from the current code according to the given value, which can be achieved using a simple while loop.
Corrected Code:
<?php
class Node {
public $value;
public $right = null;
public $left = null;
function __construct($value) {
$this->value = $value;
}
}
class BinaryTree {
public $root;
function __construct() {
$this->root = null;
}
function create($value) {
$newNode = new Node($value);
if ($this->root === null) {
$this->root = $newNode;
return $newNode; //no warning
}
$current = $this->root;
while($current !== null){
if($current->value > $value){
if($current->left === null){
$current->left = $newNode;
break;
}else{
$current = $current->left;
}
}else if($current->value < $value){
if($current->right === null){
$current->right = $newNode;
break;
}else{
$current = $current->right;
}
}else{
throw new \Exception("Node with $value already exists.");
}
}
return $newNode;
}
function preOrder() {
$visited = [];
$current = $this->root;
$this->traversePreOrder($current,$visited);
return $visited;
}
function traversePreOrder($node,&$visited) {
array_push($visited, $node->value);
if ($node->left !== null) $this->traversePreOrder($node->left,$visited);
if ($node->right !== null) $this->traversePreOrder($node->right,$visited);
}
function postOrder() {
$visited = [];
$current = $this->root;
$this->traversePostOrder($current,$visited);
return $visited;
}
function traversePostOrder($node,&$visited) {
if ($node->left !== null) $this->traversePostOrder($node->left,$visited);
if ($node->right !== null) $this->traversePostOrder($node->right,$visited);
array_push($visited, $node->value);
}
function inOrder() {
$visited = [];
$current = $this->root;
$this->traverseInOrder($current,$visited);
return $visited;
}
function traverseInOrder($node,&$visited) {
if ($node->left != null) $this->traverseInOrder($node->left,$visited);
array_push($visited, $node->value);
if ($node->right !== null) $this->traverseInOrder($node->right,$visited);
}
}
$tree = new BinaryTree();
$tree->create(50);
$tree->create(30);
$tree->create(45);
$tree->create(12);
$tree->create(29);
echo "inOrder: ". implode(",",$tree->inOrder()),PHP_EOL;
echo "preOrder: ". implode(",",$tree->preOrder()),PHP_EOL;
echo "postOrder: ". implode(",",$tree->postOrder()),PHP_EOL;
Online Demo
I'm trying to fetch an item from my queue, prioritizing items with girl names in them. I've ran into memory issues so started using chunk instead of QueueItem::all() but I'm still getting timeouts and the endpoint is taking at least 30 seconds to process if it doesn't time out before that.
Here is the controller
public function __invoke()
{
$queueItem = null;
QueueItem::chunk(1000, function ($queueItems) use ($queueItem) {
foreach ($queueItems as $item) {
if ($queueItem != null || QueueHelpers::hasGirlsName($item->item)) {
$queueItem = $item;
break;
}
}
});
if (! $queueItem instanceof QueueItem)
{
$queueItem = QueueItem::orderBy('id', 'DESC')->first();
}
if ($queueItem == null) {
abort(404);
}
$queueItem->delete();
return new QueueResource($queueItem);
}
Here is the QueueHelpers::hasGirlsName method
public static function hasGirlsName($item): bool
{
foreach (self::$girlNames as $name)
{
if (stripos($item, $name) !== false) {
return true;
}
}
return false;
}
My $girlNames have around 500+ items in them but no more than 2,000.
I want to get the main parent of chosen subcategory. I made function which recursively loop the databasse and it even get the ID but I can only echo it. I cannot return it into variable. I need to work further with the returned ID. Here is my code.
public function check_parent($parent)
{
$q = $this->db->get_where('ci_categories', array('cat_id'=>$parent));
$r = $q->row();
if ($r->cat_child > 0)
{
$this->check_parent($r->cat_child);
} else {
echo $parent;
}
}
When I use return $parent in else I get null. Any ideas ?
if you want to return the value you should return it on both places
public function check_parent($parent)
{
$q = $this->db->get_where('ci_categories', array('cat_id'=>$parent));
$r = $q->row();
if ($r->cat_child > 0)
{
return $this->check_parent($r->cat_child);
} else {
return $parent;
}
I am trying to implement a linked list using PHP.
I have completed some part of it, however, I am not sure if my code is correct or not.
Could you please advise how to add/delete a specific node at specified index?
Please refer to the below codes: (3 files in total)
1) [ListNode.class.php]
The structure of node I defined:
<?php
class ListNode
{
protected $next; // Next node in the list
protected $value; // Value of the node
// Constructor
// Input: Value of the node
public function __construct($value)
{
$this->value = $value;
$this->next = NULL;
}
public function __get($name)
{
return $this->$name;
}
public function __set($name, $value)
{
$this->$name = $value;
}
// Return the node as string
public function toString()
{
return $this->value . "\n";
}
}
2) [LinkList.class.php]
The linked list and the operations that I am not finished with:
<?php
require('ListNode.class.php');
class LinkedList
{
protected $first; // First node of the list
protected $last; // Last node of the list
protected $count; // Total numbers of nodes in the list
// Constructor
// Input: Array of values (Optional)
public function __construct($values = array())
{
$this->first = null;
$this->last = null;
$this->count = 0;
foreach ($values as $value) {
$this->add($value);
}
}
public function isEmpty()
if ($this->sizeOf() !== 0)
return ($this->first == NULL);
}
// Add a node at the beginning of the list
public function add($value)
{
$link = new ListNode($value);
$link->next = $this->first;
$this->first = &$link;
if($this->last == NULL)
$this->last = &$link;
$this->count++;
}
// Add a node at the specified index
public function addAtIndex($value, $index)
{
}
// Remove a node at the end of the list
public function remove()
{
if($this->first != NULL)
{
if($this->first->next == NULL)
{
$this->first == NULL;
$this->cout--;
}
else
{
$previous = $this->first;
$current = $this->first->next;
while($current->next != NULL)
{
$previous = $current;
$current = $current->next;
$previous->next = NULL;
$this->count--;
}
}
// Remove a node at the specified index
public function removeAtIndex($index)
{
}
// Return the value of the first node
public function getNode()
{
}
// Return the value of the node at the specified index
public function getNodeAtIndex($index)
{
if($index <= $this->count)
{
$current = $this->firstNode;
$pos = 1;
while($pos != $index)
{
if($current->next == NULL)
return null;
else
$current = $current->next;
$pos++;
}
return $current->value;
}
else
return NULL;
}
// Return the number of nodes
public function sizeOf()
{
return $this->count;
}
// Return the list as string
public function toString()
{
$list = "";
$node = $this->first;
while ($node != null) {
$list .= $node->toString();
$node = $node->next;
}
return $list;
}
}
If you need a linked list, why not use the Standard PHP Library? There you have SplDoublyLinkedList with all the functionality that you need:
add node at index: offsetSet()
delete node at index: offsetUnset()
Those methods are part of the implemented ArrayAccess interface, this means you don't have to call them directly but can do something like that:
$list = new SplDoublyLinkedList;
$list->push('item 1');
$list->push('item 2');
$list->push('item 3');
echo $list[1];
unset($list[1]);
foreach($list as $index => $value) {
echo "\n$index: $value";
}
Output:
item 2
0: item 1
1: item 3
friends. I know, there are many questions here already on these iterators.
I've read something, and I'm not a beginner... but my mind is somewhat stuck on this. Please, help me to comprehend how I use iterators in practice.
Suppose, I have an ORM object that can select instances from database. And one instance contains fields and can insert, uodate etc. As usual.
I want to iterate through all objects of a type, but as there can be plenty of them, I prefer to select them by "pages". My code:
$limit = 100;
$offset = 0;
do
{
$recs = $orm->select($filter, $sorting, $limit , $offset);
$offset += $limit;
foreach ($recs as $rec)
{
// doing something with record
}
}
while (count($recs) == $limit);
I feel that iterator paradigm is what suits here, but what interface is better to implement in this case or maybe some base SPL class?
UPDATE
Ideally code above with iterator may look like:
$iterator = new ORMPagedIterator($ormobject, $filter, $sorting);
foreach ($iterator as $rec)
{
// do something with record
}
E.g. all that page by page behavior is inside the iterator.
I would use an Iterator that iterates over another Iterator and asks for a next Iterator once it reaches the end of the previous Iterator... ok, sounds a mo complicated than it actually is:
<?php
$limit = 100;
$offset = 0;
$iter = new NextIteratorCallbackIterator(function($i) use ($orm, $limit, &$offset) {
printf("selecting next bunch at offset %d\n", $offset);
$recs = $orm->select($filter, $sorting, $limit , $offset);
$offset += $limit;
if ($recs) {
return new ArrayIterator($recs);
}
return null; // end reached
});
foreach ($iter as $rec) {
// do something with record
}
?>
And here is a sample Implementation of that NextIteratorCallbackIterator:
<?php
class NextIteratorCallbackIterator implements Iterator {
private $_iterator = null;
private $_count = 0;
private $_callback;
public function __construct($callback) {
if (!is_callable($callback)) {
throw new Exception(__CLASS__.": callback must be callable");
}
$this->_callback = $callback;
}
public function current() {
return $this->_iterator !== null ? $this->_iterator->current() : null;
}
public function key() {
return $this->_iterator !== null ? $this->_iterator->key() : null;
}
public function next() {
$tryNext = ($this->_iterator === null);
do {
if ($tryNext) {
$tryNext = false;
$this->_iterator = call_user_func($this->_callback, ++$this->_count);
}
elseif ($this->_iterator !== null) {
$this->_iterator->next();
if ($this->_iterator->valid() == false) {
$tryNext = true;
}
}
} while ($tryNext);
}
public function rewind() {
$this->_iterator = call_user_func($this->_callback, $this->_count = 0);
}
public function valid () {
return $this->_iterator !== null;
}
}
?>
UPDATE: Your ORMPagedIterator can be implemented using NextIteratorCallbackIterator as easy as:
<?php
class ORMPagedIterator implements IteratorAggregate {
function __construct($orm, $filter, $sorting, $chunksize = 100) {
$this->orm = $orm;
$this->filter = $filter;
$this->sorting = $sorting;
$this->chunksize = $chunksize;
}
function iteratorNext($i) {
$offset = $this->chunksize * $i;
$recs = $this->orm->select($this->filter, $this->sorting, $this->chunksize, $offset);
if ($recs) {
return new ArrayIterator($recs);
}
return null; // end reached
}
function getIterator() {
return new NextIteratorCallbackIterator(array($this,"iteratorNext"));
}
}
?>