Doctrine2: Syncing Collections / Adding multiple Elements of which some might already exist - php

So i have a string, representing several objects (tags in this case)
i.e.: "php,mysql,doctrine2"
Let's say my database already has "php" and "doctrine2".
Now i want the best way to add the missing elemets (in this case mysql).
Should i create an object for every element and just use persist/sync or something, or is there a better way?
I need all the objects at the end anyway to add them to a new object (with a simple many-to-many relation) anyway.
I'd be happy about any suggestions.

1) Pull out all your tag names with a single query into an array
2) Use array_filter along with a closure to detect tags not present in the dataset
3) Create an insert for the new tags
$currentTags = getCurrentTagsArray();
$newTags = explode(',', 'php,mysql,doctrine2');
$newTagsToSave = array_filter($currentTags, function($item) use ($newTags){
if (in_array($item, $newTags))
{
return false;
}
return true;
});
Or...
You can use Doctrine 2's ArrayCollection wrapper (\Doctrine\Common\Collections\ArrayCollection()) it has pretty much the same implementation above as a filter method (you still need to pass the closure).
$myCollection->filter($closure);

I had a similar problem where I had to synchronize an entity collection with an external source. However, my problem required not only additions, but also updates and deletes. I used code to diff the ArrayCollection with another array, and call CRUD methods add based on the differences. As far as I can tell from the docs, doctrine doesn't natively handle this. Average performance should be O(n) but takes some memory.
/**
* #param array $source - the array we are starting with
* #param array $new - the array we want to end with
* #param $fnHash - function used to determine object equality, not based on object id
* #param $fnUpdate - function to perform update of existing object, takes current object and new object as params
* #param $fnAdd - function to perform insert
* #param $fnDelete - function to perform delete
*/
public static function syncArrays(array $source, array $new,
$fnHash, $fnUpdate, $fnAdd, $fnDelete)
{
// make modifiable array copies mapped by hashes of the elements
$sourceKeys = array_map($fnHash, $source);
$hasKeys =count($sourceKeys) > 0;
$newarray = ($hasKeys) ? array_combine(array_map($fnHash, $new), $new) : $new;
if ($hasKeys) { // true => may have updates or deletes
$sourcearray = array_combine($sourceKeys, $source);
// updates
foreach ($sourceKeys as $hashkey) {
if (isset($sourcearray[$hashkey]) && isset($newarray[$hashkey])) {
$fnUpdate($sourcearray[$hashkey], $newarray[$hashkey]);
unset($sourcearray[$hashkey]);
unset($newarray[$hashkey]);
}
}
// deletes
foreach ($sourcearray as $entity) {
$fnDelete($entity);
}
}
//adds
foreach ($newarray as $entity) {
$fnAdd($entity);
}
}
The way I call it to update my doctrine association $parentEntity->getPayments() is:
ArrayHelper::syncArrays($parentEntity->getPayments()->toArray(), $newPayments,
function($entity) {return $a->getName();}, // hash function
function($current, $new) {
$current->setTotal($new->getTotal()); // update function
},
function($a) use ($parent, $manager) {
$parent->addVendorPaymentObject($a); // add function
$manager->persist($a);
},
function($a) use ($manager) { // delete function
$manager->remove($a);
}
);

Related

updating several records with one request in laravel

Some quick context:
I have a sql table and a eloquent model for JobCards and each JobCard has several Operations belonging to it. I have a table and model for Operations. The users of my application browse and edit JobCards, but when I say editing a Jobcard this can include editing Operations associated with a JobCard. I have a page where a user can edit the Operations for a certain JobCard, I submit the the data as an array of Operations.
I want a clean way to update the data for the Operations of a JobCard. There are 3 different actions I may or may not need to do:
Update an existing Operation with new data
Create a new Operation
Delete an Operatation
I tried dealing with the first 2 and things are getting messy already. I still need a way of deleting an Operation if it is not present in the array sent in the request.
Heres my code:
public function SaveOps(Request $a)
{
$JobCardNum = $a -> get('JobCardNum');
$Ops = $a -> get('Ops');
foreach ($Ops as $Op) {
$ExistingOp = JobCardOp::GetOp($JobCardNum, $Op['OpNum'])->first();
if(count($ExistingOp)==0) {
$NewOp = new JobCardOp;
$NewOp -> JobCardNum = $JobCardNum;
$NewOp -> fill($Op);
$NewOp -> save();
$this->UpdateNextOpStatus($JobCardNum, $NewOp);
}
else {
$ExistingOp -> fill($Op);
$ExistingOp -> save();
}
}
Can anyone help with the deletion part and/or help make my code tidier.
This is how your method should look like. Please note that, I added a new method getCache($JobCardNum) this method will get an array of operations per job card (assuming that your model is designed to be related this way) this method will go to the DB only once, to get all the Operations that you need for this method call instead of getting them one-by-one (in the foreach loop), this way you make sure that the expensive call to the DB is done only once, on the other hand you got your JobCard's operations in the form of an array ready to compare with the new ones (coming in the request), the return of this method will be in the form of (key=>value with the key being the operation number and the value being the operation object it self).
/**
* This function will get you an array of current operations in the given job card
* #param $JobCardNum
* #return array
*/
public function getCache($JobCardNum)
{
/**
* asuming that the relation in your model is built that way. if not you should then
* use JobCardOp::all(); (Not recommended because it will get a lot of unnecessary
* data )
*/
$ExistingOps = JobCardOp::where('job_card_id', '=', $JobCardNum);
$opCache = array();
foreach ($ExistingOps as $Op) {
$opCache[(string)$Op->OpNum] = $Op;
}
return $opCache;
}
public function SaveOps(Request $a)
{
$strOpNum = (string)$Op['OpNum'];
$JobCardNum = $a->get('JobCardNum');
$Ops = $a->get('Ops');
$opCache = $this->getCache($JobCardNum);
foreach ($Ops as $Op) {
if (!isset($opCache[$strOpNum])) {
$NewOp = new JobCardOp;
$NewOp->JobCardNum = $JobCardNum;
$NewOp->fill($Op);
$NewOp->save();
$this->UpdateNextOpStatus($JobCardNum, $NewOp);
} else {
$ExistingOp = $opCache[$strOpNum];
$ExistingOp->fill($Op);
$ExistingOp->save();
}
unset($opCache[$strOpNum]);
}
/*
* at this point any item in the $opCache array must be deleted because it was not
* matched in the previous for loop that looped through the requested operations :)
*/
foreach ($opCache as $op) {
$op->delete();
}
}

Ordering Doctrine Collection based on associated Entity when it is not possible to use the #orderBy annotation

I would like to understand the best way to order a Doctrine Collection based on associated Entity. In this case, it is not possible to use the #orderBy annotation.
I have found 5 solutions on the Internet.
1) Adding a method to the AbstractEntity (according to Ian Belter https://stackoverflow.com/a/22183527/1148260)
/**
* This method will change the order of elements within a Collection based on the given method.
* It preserves array keys to avoid any direct access issues but will order the elements
* within the array so that iteration will be done in the requested order.
*
* #param string $property
* #param array $calledMethods
*
* #return $this
* #throws \InvalidArgumentException
*/
public function orderCollection($property, $calledMethods = array())
{
/** #var Collection $collection */
$collection = $this->$property;
// If we have a PersistentCollection, make sure it is initialized, then unwrap it so we
// can edit the underlying ArrayCollection without firing the changed method on the
// PersistentCollection. We're only going in and changing the order of the underlying ArrayCollection.
if ($collection instanceOf PersistentCollection) {
/** #var PersistentCollection $collection */
if (false === $collection->isInitialized()) {
$collection->initialize();
}
$collection = $collection->unwrap();
}
if (!$collection instanceOf ArrayCollection) {
throw new InvalidArgumentException('First argument of orderCollection must reference a PersistentCollection|ArrayCollection within $this.');
}
$uaSortFunction = function($first, $second) use ($calledMethods) {
// Loop through $calledMethods until we find a orderable difference
foreach ($calledMethods as $callMethod => $order) {
// If no order was set, swap k => v values and set ASC as default.
if (false == in_array($order, array('ASC', 'DESC')) ) {
$callMethod = $order;
$order = 'ASC';
}
if (true == is_string($first->$callMethod())) {
// String Compare
$result = strcasecmp($first->$callMethod(), $second->$callMethod());
} else {
// Numeric Compare
$difference = ($first->$callMethod() - $second->$callMethod());
// This will convert non-zero $results to 1 or -1 or zero values to 0
// i.e. -22/22 = -1; 0.4/0.4 = 1;
$result = (0 != $difference) ? $difference / abs($difference): 0;
}
// 'Reverse' result if DESC given
if ('DESC' == $order) {
$result *= -1;
}
// If we have a result, return it, else continue looping
if (0 !== (int) $result) {
return (int) $result;
}
}
// No result, return 0
return 0;
};
// Get the values for the ArrayCollection and sort it using the function
$values = $collection->getValues();
uasort($values, $uaSortFunction);
// Clear the current collection values and reintroduce in new order.
$collection->clear();
foreach ($values as $key => $item) {
$collection->set($key, $item);
}
return $this;
}
2) Creating a Twig extension, if you need the sorting just in a template (according to Kris https://stackoverflow.com/a/12505347/1148260)
use Doctrine\Common\Collections\Collection;
public function sort(Collection $objects, $name, $property = null)
{
$values = $objects->getValues();
usort($values, function ($a, $b) use ($name, $property) {
$name = 'get' . $name;
if ($property) {
$property = 'get' . $property;
return strcasecmp($a->$name()->$property(), $b->$name()->$property());
} else {
return strcasecmp($a->$name(), $b->$name());
}
});
return $values;
}
3) Transforming the collection into an array and then sorting it (according to Benjamin Eberlei https://groups.google.com/d/msg/doctrine-user/zCKG98dPiDY/oOSZBMabebwJ)
public function getSortedByFoo()
{
$arr = $this->arrayCollection->toArray();
usort($arr, function($a, $b) {
if ($a->getFoo() > $b->getFoo()) {
return -1;
}
//...
});
return $arr;
}
4) Using ArrayIterator to sort the collection (according to nifr https://stackoverflow.com/a/16707694/1148260)
$iterator = $collection->getIterator();
$iterator->uasort(function ($a, $b) {
return ($a->getPropery() < $b->getProperty()) ? -1 : 1;
});
$collection = new ArrayCollection(iterator_to_array($iterator));
5) Creating a service to gather the ordered collection and then replace the unordered one (I have not an example but I think it is pretty clear). I think this is the ugliest solution.
Which is the best solution according to you experience? Do you have other suggestions to order a collection in a more effective/elegant way?
Thank you very much.
Premise
You proposed 5 valid/decent solutions, but I think that all could be reduced down to two cases, with some minor variants.
We know that sorting is always O(NlogN), so all solution have theoretically the same performance. But since this is Doctrine, the number of SQL queries and the Hydration methods (i.e. converting data from array to object instance) are the bottlenecks.
So you need to choose the "best method", depending on when you need the entities to be loaded and what you'll do with them.
These are my "best solutions", and in a general case I prefer my solution A)
A) DQL in a loader/repository service
Similar to
None of your case (somehow with 5, see the final notes note). Alberto Fernández pointed you in the right direction in a comment.
Best when
DQL is (potentially) the fastest method, since delegate sorting to DBMS which is highly optimized for this. DQL also gives total controls on which entities to fetch in a single query and the hydrations mode.
Drawbacks
It is not possible (AFAIK) to modify query generated by Doctrine Proxy classes by configuration, so your application need to use a Repository and call the proper method every time you load your entities (or override the default one).
Example
class MainEntityRepository extends EntityRepository
{
public function findSorted(array $conditions)
{
$qb = $this->createQueryBuilder('e')
->innerJoin('e.association', 'a')
->orderBy('a.value')
;
// if you always/frequently read 'a' entities uncomment this to load EAGER-ly
// $qb->select('e', 'a');
// If you just need data for display (e.g. in Twig only)
// return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
return $qb->getQuery()->getResult();
}
}
B) Eager loading, and sorting in PHP
Similar to case
Case 2), 3) and 4) are just the same thing done in different place. My version is a general case which apply whenever the entities are fetched. If you have to choose one of these, then I think that solution 3) is the most convenient, since don't mess with the entity and is always available, but use EAGER loading (read on).
Best when
If the the associated entities are always read, but it is not possible (or convenient) to add a service, then all entities should loaded EAGER-ly. Sorting then can be done by PHP, whenever it makes sense for the application: in an event listener, in a controller, in a twig template... If the entities should be always loaded, then an event listener is the best option.
Drawbacks
Less flexible than DQL, and sorting in PHP may be a slow operation when the collection is big. Also, the entities need to be hydrated as Object which is slow, and is overkill if the collection is not used for other purpose. Beware of lazy-loading, since this will trigger one query for every entity.
Example
MainEntity.orm.xml:
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping>
<entity name="MainEntity">
<id name="id" type="integer" />
<one-to-many field="collection" target-entity="LinkedEntity" fetch="EAGER" />
<entity-listeners>
<entity-listener class="MainEntityListener"/>
</entity-listeners>
</entity>
</doctrine-mapping>
MainEntity.php:
class MainEntityListener
{
private $id;
private $collection;
public function __construct()
{
$this->collection = new ArrayCollection();
}
// this works only with Doctrine 2.5+, in previous version association where not loaded on event
public function postLoad(array $conditions)
{
/*
* From your example 1)
* Remember that $this->collection is an ArryCollection when constructor is called,
* but a PersistentCollection when are loaded from DB. Don't recreate the instance!
*/
// Get the values for the ArrayCollection and sort it using the function
$values = $this->collection->getValues();
// sort as you like
asort($values);
// Clear the current collection values and reintroduce in new order.
$collection->clear();
foreach ($values as $key => $item) {
$collection->set($key, $item);
}
}
}
Final Notes
I won't use case 1) as is, since is very complicated and introduce inheritance which reduce encapsulation. Also, I think that it has the same complexity and performance of my example.
Case 5) is not necessarily bad. If "the service" is the application repository, and it use DQL to sort, then is my first best case. If is a custom service only to sort a collection, then I think is definitely not a good solution.
All the codes I wrote here is not ready for "copy-paste", since my objective was to show my point of view. Hope it would be a good starting point.
Disclaimer
These are "my" best solutions, as I do it in my works. Hope will help you and others.

Is there another way than passing by reference to do what I want

I have the following data structure:
A Contract has an array projects, which can have X number of projects. Each Project has an array, subProjects, which contain the same Project type, so theoretically you could have an infinite tree of Project-SubProjects-Project...
Anyway, each project has a unique ID, and I need to search for a given project AND make a modification to that project, starting at the top level, and then store the changed contract back to my session. Currently, I'm doing it via a recursive function that returns a reference to the project it finds, but the more I'm searching, the more it seems people don't like PHP references. I'm not sure why, could someone explain the problems? Is there a better way to do what I want?
Some code:
// Get the associative array version of the contract (it's stored as JSON)
$contract = json_decode($contract, true);
if(array_key_exists('projects', $contract))
{
$resultProject = &$this->findProject($contract['projects'], $projectId);
if($resultProject)
{
$resultProject[$inputData['propertyName']] = $inputData['value'];
\Session::put('workingContract', json_encode($contract));
// return 200
}
}
// Return 404
/**
* Performs a depth-first search to find a project.
*
* #param array $projects
* #param $projectId
* #return null
*/
private function &findProject(array &$projects, $projectId)
{
foreach($projects as &$project)
{
if($project['_id']['$id'] == $projectId)
{
return $project;
}
if(array_key_exists('subProjects', $project))
{
$result = &$this->findProject($project['subProjects'], $projectId);
return $result;
}
}
$null = null; // TODO: shitty hack for inability to return null when function returns a reference. Need to rethink use of references in general. Is there another way???
return $null;
}
Why not just create an array with all your projects (a flat array), indexed by ID. Let each Project object have an ->id property that you can refer to. Problem solved?
Also, if the Project doesn't exist in the flat array, I see absolutely no problem in returning null.
class Contract {
private $projects_flat = array();
....
private function get_project($id) {
return (isset($this->projects_flat[$id]) ? $this->projects_flat[$id] : null)
}
}

append a related Model in Phalcon

I wrote a vcard class with Phalcon in PHP. The vCard Model is initialized like this.
// Inside the BS_VCard class
public function initialize(){
$this->hasMany("id","BS_VCardElement","vCardId",array(
"alias" => "elements",
'foreignKey' => array(
'action' => Phalcon\Mvc\Model\Relation::ACTION_CASCADE
)
));
}
Its elements are initialized like this
// Inside the BS_VCardElement class
public function initialize(){
$this->belongsTo("vCardId","BS_VCard","id",array("alias" => "vCard"));
...
}
If a user reads a vCard and adds another element, it doesn't work as expected. To simplify the use I added some fascade methods like this
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
// This doesn't work
$this->elements[] = $element;
}
The Docs/Storing related records do not explain how to append fresh data like this to the related table.
I also tried this
$this->elements[] = array_merge($this->elements,array($element));
But the save method seems to ignore the added element. Save() returns true.
This question has been asked a couple of months ago but since I ran into a similar issue I decided to share my results anyway.
And here's what I found. Lower case aliases ('elements') don't seem to work whereas upper case aliases ('Elements') do.
To add one element you can do this;
$this->Elements = $element;
To add multiple elements you can do this;
$elements = array($element1, $element2);
$this->Elements = $elements;
After that you have to save the vcard before accessing the elements again. If you don't, phalcon will just return a result set with only the elements already in the database. (Not sure if this can be changed somehow.)
And here's the documentation (where all this is not mentioned): http://docs.phalconphp.com/en/latest/reference/models.html#storing-related-records
According to the Phalcon source code, the Resultset object is immutible.
/**
* Resultsets cannot be changed. It has only been implemented to
* meet the definition of the ArrayAccess interface
*
* #param int index
* #param \Phalcon\Mvc\ModelInterface value
*/
public function offsetSet(var index, var value)
{
throw new Exception("Cursor is an immutable ArrayAccess object");
}
It appears that replacing the element with an array is the only way to implement an "append" or modification of the resultset (other than delete which IS supported).
Of course this breaks the \Phalcon\Mvc\Model::_preSaveRelatedRecords() because the function ignores the class properties and refetches the related from the Model Manager (and resets the model::$element attribute at the end).
I feel frustrated by this because appending objects to a collection seems like a very common task and not having a clear method in which to add new items to a parent seems like a design flaw.
I think related elements might have some magic functionality invoked when you set the properties, so simply using $this->elements[] (evidently) doesn't work. Perhaps try re-setting the entire variable:
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
$elements = $this->elements;
$elements[] = $element;
$this->elements = $elements;
}

Does PHP have built-in data structures?

I'm looking at the PHP Manual, and I'm not seeing a section on data structures that most languages have, such as lists and sets. Am I just blind or does PHP not have anything like this built in?
The only native data structure in PHP is array. Fortunately, arrays are quite flexible and can be used as hash tables as well.
http://www.php.net/array
However, there is SPL which is sort of a clone of C++ STL.
http://www.php.net/manual/en/book.spl.php
PHP offers data structures through the Standard PHP Library (SPL) basic extension, which is available and compiled by default in PHP 5.0.0.
The data structures offered are available with PHP 5 >= 5.3.0, and includes:
Doubly Linked Lists
A Doubly Linked List (DLL) is a list of nodes linked in both directions to each others. Iterator’s operations, access to both ends, addition or removal of nodes have a cost of O(1) when the underlying structure is a DLL. It hence provides a decent implementation for stacks and queues.
SplDoublyLinkedList class
SplStack class
SplQueue class
Heaps
Heaps are tree-like structures that follow the heap-property: each node is greater than or equal to its children, when compared using the implemented compare method which is global to the heap.
SplHeap class
SplMaxHeap class
SplMinHeap class
SplPriorityQueue class
Arrays
Arrays are structures that store the data in a continuous way, accessible via indexes. Don’t confuse them with PHP arrays: PHP arrays are in fact implemented as ordered hashtables.
SplFixedArray class
Map
A map is a datastructure holding key-value pairs. PHP arrays can be seen as maps from integers/strings to values. SPL provides a map from objects to data. This map can also be used as an object set.
SplObjectStorage class
Source: http://php.net/manual/en/spl.datastructures.php
PHP 7 introduced an extension called ds providing specialized data structures as an alternative to the array.
The ds,
uses the Ds\ namespace.
has 3 interfaces namely,Collection, Sequence and Hashable
has 8 classes namely, Vector, Deque,Queue, PriorityQueue, Map, Set, Stack, and Pair
For more information checkout the Manual and also This blog post has some awesome information including benchmarks.
The associative array can be used for most basic data structures hashtable, queue, stack. But if you want something like a tree or heap I don't think they exist by default but I'm sure there are free libraries anywhere.
To have an array emulate a stack use array_push() to add and array_pop() to take off
To have an array emulate a queue use array_push() to enqueue and array_shift() to dequeue
An associative array is a hash by default. In PHP they are allowed to have strings as indexes so this works as expected:
$array['key'] = 'value';
Finally, you can kind of emulate a binary tree with an array with the potential to have wasted space. Its useful if you know you're going to have a small tree. Using a linear array, you say for any index (i) you put its left child at index (2i+1) and right child at index (2i+2).
All of these methods are covered nicely in this article on how to make JavaScript arrays emulate higher level data structures.
PHP has arrays, which are actually associative arrays and can also be used as sets. Like many interpreted languages, PHP offers all this under one hood instead of providing different explicit data types.
E.g.
$lst = array(1, 2, 3);
$hsh = array(1 => "This", 2 => "is a", 3 => "test");
Also, take a look in the manual.
PHP's array doubles as both a list and a dictionary.
$myArray = array("Apples", "Oranges", "Pears");
$myScalar = $myArray[0] // == "Apples"
Or to use it as an associative array:
$myArray = array("a"=>"Apples", "b"=>"Oranges", "c"=>"Pears");
$myScalar = $myArray["a"] // == "Apples"
I think you might want to be a bit more specific, when you say data structures my mind goes in a few directions...
Arrays - They are certainly well documented and available in. (http://us.php.net/manual/en/book.array.php)
SQL Data - Depends on the database you are using, but most are available. (http://us.php.net/manual/en/book.mysql.php)
OOP - Depending on the version objects can be designed and implemented. (http://us.php.net/manual/en/language.oop.php) I had to search for OOP to find this on the php site.
Of course PHP has data structures. The array in php is incredibly flexible. Some examples:
$foo = array(
'bar' => array(1,'two',3),
'baz' => explode(" ", "Some nice words")
);
Then you have an absolute plethora of array functions available to map/filter/walk/etc the structures, or convert, flip, reverse, etc.
You can always create your own if you don't feel PHP includes a specific type of data structure. For example, here is a simple Set data structure backed by an Array.
class ArraySet
{
/** Elements in this set */
private $elements;
/** the number of elements in this set */
private $size = 0;
/**
* Constructs this set.
*/
public function ArraySet() {
$this->elements = array();
}
/**
* Adds the specified element to this set if
* it is not already present.
*
* #param any $element
*
* #returns true if the specified element was
* added to this set.
*/
public function add($element) {
if (! in_array($element, $this->elements)) {
$this->elements[] = $element;
$this->size++;
return true;
}
return false;
}
/**
* Adds all of the elements in the specified
* collection to this set if they're not already present.
*
* #param array $collection
*
* #returns true if any of the elements in the
* specified collection where added to this set.
*/
public function addAll($collection) {
$changed = false;
foreach ($collection as $element) {
if ($this->add($element)) {
$changed = true;
}
}
return $changed;
}
/**
* Removes all the elements from this set.
*/
public function clear() {
$this->elements = array();
$this->size = 0;
}
/**
* Checks if this set contains the specified element.
*
* #param any $element
*
* #returns true if this set contains the specified
* element.
*/
public function contains($element) {
return in_array($element, $this->elements);
}
/**
* Checks if this set contains all the specified
* element.
*
* #param array $collection
*
* #returns true if this set contains all the specified
* element.
*/
public function containsAll($collection) {
foreach ($collection as $element) {
if (! in_array($element, $this->elements)) {
return false;
}
}
return true;
}
/**
* Checks if this set contains elements.
*
* #returns true if this set contains no elements.
*/
public function isEmpty() {
return count($this->elements) <= 0;
}
/**
* Get's an iterator over the elements in this set.
*
* #returns an iterator over the elements in this set.
*/
public function iterator() {
return new SimpleIterator($this->elements);
}
/**
* Removes the specified element from this set.
*
* #param any $element
*
* #returns true if the specified element is removed.
*/
public function remove($element) {
if (! in_array($element, $this->elements)) return false;
foreach ($this->elements as $k => $v) {
if ($element == $v) {
unset($this->elements[$k]);
$this->size--;
return true;
}
}
}
/**
* Removes all the specified elements from this set.
*
* #param array $collection
*
* #returns true if all the specified elemensts
* are removed from this set.
*/
public function removeAll($collection) {
$changed = false;
foreach ($collection as $element) {
if ($this->remove($element)) {
$changed = true;
}
}
return $changed;
}
/**
* Retains the elements in this set that are
* in the specified collection. If the specified
* collection is also a set, this method effectively
* modifies this set into the intersection of
* this set and the specified collection.
*
* #param array $collection
*
* #returns true if this set changed as a result
* of the specified collection.
*/
public function retainAll($collection) {
$changed = false;
foreach ($this->elements as $k => $v) {
if (! in_array($v, $collection)) {
unset($this->elements[$k]);
$this->size--;
$changed = true;
}
}
return $changed;
}
/**
* Returns the number of elements in this set.
*
* #returns the number of elements in this set.
*/
public function size() {
return $this->size;
}
/**
* Returns an array that contains all the
* elements in this set.
*
* #returns an array that contains all the
* elements in this set.
*/
public function toArray() {
$elements = $this->elements;
return $elements;
}
}
PHP can also have an array of arrays which is called a "multidimensional array" or "matrix". You can have 2-dimensional arrays, 3-dimensional arrays, etc.

Categories