When writing controllers for Symfony 2, I often need to pass quite a few variables to the template like return array('param1' => $param1, 'anotherBigParam' => $anotherBigParam, 'yetAnotherParam' => $yetAnotherParam);
With many parameters this ends up really long and ugly, so I thought about creating a helper function for it:
public function indexAction()
{
$param1 = 'fee';
$anotherBigParam = 'foe';
$yetAnotherParam = 'fum';
return $this->vars('param1', 'anotherBigParam', 'yetAnotherParam');
}
private function vars() {
$arr = array();
foreach(func_get_args() as $arg) {
$arr[$arg] = $$arg;
}
return $arr;
}
Is there some kind of drawback or risk from doing this? Does PHP or Symfony 2 already provide a better or cleaner way to achieve the same result?
There is a native way of doing it: compact
$one = 'ONE';
$two = 'TWO';
$a = compact( 'one', 'two' );
print_r( $a );
/*
Array
(
[one] => ONE
[two] => TWO
)
*/
You're looking for compact.
public function indexAction()
{
$param1 = 'fee';
$anotherBigParam = 'foe';
$yetAnotherParam = 'fum';
return compact('param1', 'anotherBigParam', 'yetAnotherParam');
}
Related
This question already has answers here:
Method Chains PHP OOP
(3 answers)
Closed 8 years ago.
I have this working sample code as an example...
function get_childs() {
$array = array(1 => 'item1', 2 => 'item2', 3 => 'item3');
return $array;
}
function add( $array, $item ) {
$array[] = $item;
return $array;
}
function array_delete( $array, $key ) {
unset( $array[$key] );
return $array;
}
$result_array = array_delete( add( get_childs(), 'test' ), 2 );
print_r( $result_array );
Change to arrows instead
Right now a part of the code looks like this (quite ugly):
array_delete( add( get_childs(), 'test' ), 2 );
I have seen on the web that it is possible to do it someting like this instead:
get_childs().add('test').delete(2);
Much more beautiful. How is it done?
A sidenote
I have seen that the functions called this way can be repeated like this:
get_childs().add('something1').add('something2').add('something3');
The easiest way is move this functionality to class, eg:
class MyCollection
{
private $arr;
public function create_childs()
{
$this->arr = array(1 => 'item1', 2 => 'item2', 3 => 'item3');
return $this;
}
public function get_childs()
{
return $this->arr;
}
public function add($item)
{
$this->arr[] = $item;
return $this;
}
public function delete($key)
{
unset($this->arr[$key]);
return $this;
}
}
$collection = new MyCollection();
print_r($collection->create_childs()->add("test")->delete(2)->get_childs());
Suppose I have:
class A{
public $one;
public $two;
}
and an array with values:
array('one' => 234, 'two' => 2)
is there a way to have an instance of A filled with the right values from the array automatically?
You need to write yourself a function for that. PHP has get_object_varsDocs but no set counterpart:
function set_object_vars($object, array $vars) {
$has = get_object_vars($object);
foreach ($has as $name => $oldValue) {
$object->$name = isset($vars[$name]) ? $vars[$name] : NULL;
}
}
Usage:
$a = new A();
$vars = array('one' => 234, 'two' => 2);
set_object_vars($a, $vars);
If you want to allow for bulk-setting of attributes, you can also store them as a property. It allows you to encapsulate within the class a little better.
class A{
protected $attributes = array();
function setAttributes($attributes){
$this->attributes = $attributes;
}
public function __get($key){
return $this->attributes[$key];
}
}
#hakre version is quite good, but dangerous (suppose an id or password is in thoses props).
I would change the default behavior to that:
function set_object_vars($object, array $vars) {
$has = get_object_vars($object);
foreach ($has as $name => $oldValue) {
array_key_exists($name, $vars) ? $object->$name =$vars[$name] : NULL;
}
}
here, the previous properties that are not in the $vars array are not affected.
and if you want to set a prop to null on purpose, you can.
Yes there is.
You could use a pass thru method.
For example:
class A {
public $one, $tow;
function __construct($values) {
$this->one = $values['one'] ?: null;
$this->two = $values['two'] ?: null;
}
}
$a = new A(array('one' => 234, 'two' => 2));
Is there any method like the array_unique for objects? I have a bunch of arrays with 'Role' objects that I merge, and then I want to take out the duplicates :)
array_unique works with an array of objects using SORT_REGULAR:
class MyClass {
public $prop;
}
$foo = new MyClass();
$foo->prop = 'test1';
$bar = $foo;
$bam = new MyClass();
$bam->prop = 'test2';
$test = array($foo, $bar, $bam);
print_r(array_unique($test, SORT_REGULAR));
Will print:
Array (
[0] => MyClass Object
(
[prop] => test1
)
[2] => MyClass Object
(
[prop] => test2
)
)
See it in action here: http://3v4l.org/VvonH#v529
Warning: it will use the "==" comparison, not the strict comparison ("===").
So if you want to remove duplicates inside an array of objects, beware that it will compare each object properties, not compare object identity (instance).
Well, array_unique() compares the string value of the elements:
Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2 i.e. when the string representation is the same, the first element will be used.
So make sure to implement the __toString() method in your class and that it outputs the same value for equal roles, e.g.
class Role {
private $name;
//.....
public function __toString() {
return $this->name;
}
}
This would consider two roles as equal if they have the same name.
This answer uses in_array() since the nature of comparing objects in PHP 5 allows us to do so. Making use of this object comparison behaviour requires that the array only contain objects, but that appears to be the case here.
$merged = array_merge($arr, $arr2);
$final = array();
foreach ($merged as $current) {
if ( ! in_array($current, $final)) {
$final[] = $current;
}
}
var_dump($final);
Here is a way to remove duplicated objects in an array:
<?php
// Here is the array that you want to clean of duplicate elements.
$array = getLotsOfObjects();
// Create a temporary array that will not contain any duplicate elements
$new = array();
// Loop through all elements. serialize() is a string that will contain all properties
// of the object and thus two objects with the same contents will have the same
// serialized string. When a new element is added to the $new array that has the same
// serialized value as the current one, then the old value will be overridden.
foreach($array as $value) {
$new[serialize($value)] = $value;
}
// Now $array contains all objects just once with their serialized version as string.
// We don't care about the serialized version and just extract the values.
$array = array_values($new);
You can also serialize first:
$unique = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
As of PHP 5.2.9 you can just use optional sort_flag SORT_REGULAR:
$unique = array_unique( $array, SORT_REGULAR );
You can also use they array_filter function, if you want to filter objects based on a specific attribute:
//filter duplicate objects
$collection = array_filter($collection, function($obj)
{
static $idList = array();
if(in_array($obj->getId(),$idList)) {
return false;
}
$idList []= $obj->getId();
return true;
});
From here: http://php.net/manual/en/function.array-unique.php#75307
This one would work with objects and arrays also.
<?php
function my_array_unique($array, $keep_key_assoc = false)
{
$duplicate_keys = array();
$tmp = array();
foreach ($array as $key=>$val)
{
// convert objects to arrays, in_array() does not support objects
if (is_object($val))
$val = (array)$val;
if (!in_array($val, $tmp))
$tmp[] = $val;
else
$duplicate_keys[] = $key;
}
foreach ($duplicate_keys as $key)
unset($array[$key]);
return $keep_key_assoc ? $array : array_values($array);
}
?>
If you have an indexed array of objects, and you want to remove duplicates by comparing a specific property in each object, a function like the remove_duplicate_models() one below can be used.
class Car {
private $model;
public function __construct( $model ) {
$this->model = $model;
}
public function get_model() {
return $this->model;
}
}
$cars = [
new Car('Mustang'),
new Car('F-150'),
new Car('Mustang'),
new Car('Taurus'),
];
function remove_duplicate_models( $cars ) {
$models = array_map( function( $car ) {
return $car->get_model();
}, $cars );
$unique_models = array_unique( $models );
return array_values( array_intersect_key( $cars, $unique_models ) );
}
print_r( remove_duplicate_models( $cars ) );
The result is:
Array
(
[0] => Car Object
(
[model:Car:private] => Mustang
)
[1] => Car Object
(
[model:Car:private] => F-150
)
[2] => Car Object
(
[model:Car:private] => Taurus
)
)
sane and fast way if you need to filter duplicated instances (i.e. "===" comparison) out of array and:
you are sure what array holds only objects
you dont need keys preserved
is:
//sample data
$o1 = new stdClass;
$o2 = new stdClass;
$arr = [$o1,$o1,$o2];
//algorithm
$unique = [];
foreach($arr as $o){
$unique[spl_object_hash($o)]=$o;
}
$unique = array_values($unique);//optional - use if you want integer keys on output
This is very simple solution:
$ids = array();
foreach ($relate->posts as $key => $value) {
if (!empty($ids[$value->ID])) { unset($relate->posts[$key]); }
else{ $ids[$value->ID] = 1; }
}
You can also make the array unique using a callback function (e.g. if you want to compare a property of the object or whatever method).
This is the generic function I use for this purpose:
/**
* Remove duplicate elements from an array by comparison callback.
*
* #param array $array : An array to eliminate duplicates by callback
* #param callable $callback : Callback accepting an array element returning the value to compare.
* #param bool $preserveKeys : Add true if the keys should be perserved (note that if duplicates eliminated the first key is used).
* #return array: An array unique by the given callback
*/
function unique(array $array, callable $callback, bool $preserveKeys = false): array
{
$unique = array_intersect_key($array, array_unique(array_map($callback, $array)));
return ($preserveKeys) ? $unique : array_values($unique);
}
Sample usage:
$myUniqueArray = unique($arrayToFilter,
static function (ExamQuestion $examQuestion) {
return $examQuestion->getId();
}
);
array_unique version for strict (===) comparison, preserving keys:
function array_unique_strict(array $array): array {
$result = [];
foreach ($array as $key => $item) {
if (!in_array($item, $result, true)) {
$result[$key] = $item;
}
}
return $result;
}
Usage:
class Foo {}
$foo1 = new Foo();
$foo2 = new Foo();
array_unique_strict( ['a' => $foo1, 'b' => $foo1, 'c' => $foo2] ); // ['a' => $foo1, 'c' => $foo2]
array_unique works by casting the elements to a string and doing a comparison. Unless your objects uniquely cast to strings, then they won't work with array_unique.
Instead, implement a stateful comparison function for your objects and use array_filter to throw out things the function has already seen.
This is my way of comparing objects with simple properties, and at the same time receiving a unique collection:
class Role {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$roles = [
new Role('foo'),
new Role('bar'),
new Role('foo'),
new Role('bar'),
new Role('foo'),
new Role('bar'),
];
$roles = array_map(function (Role $role) {
return ['key' => $role->getName(), 'val' => $role];
}, $roles);
$roles = array_column($roles, 'val', 'key');
var_dump($roles);
Will output:
array (size=2)
'foo' =>
object(Role)[1165]
private 'name' => string 'foo' (length=3)
'bar' =>
object(Role)[1166]
private 'name' => string 'bar' (length=3)
If you have array of objects and you want to filter this collection to remove all duplicates you can use array_filter with anonymous function:
$myArrayOfObjects = $myCustomService->getArrayOfObjects();
// This is temporary array
$tmp = [];
$arrayWithoutDuplicates = array_filter($myArrayOfObjects, function ($object) use (&$tmp) {
if (!in_array($object->getUniqueValue(), $tmp)) {
$tmp[] = $object->getUniqueValue();
return true;
}
return false;
});
Important: Remember that you must pass $tmp array as reference to you filter callback function otherwise it will not work
I have an associative array that I might need to access numerically (ie get the 5th key's value).
$data = array(
'one' => 'something',
'two' => 'else',
'three' => 'completely'
) ;
I need to be able to do:
$data['one']
and
$data[0]
to get the same value, 'something'.
My initial thought is to create a class wrapper that implements ArrayAccess with offsetGet() having code to see if the key is numeric and act accordingly, using array_values:
class MixedArray implements ArrayAccess {
protected $_array = array();
public function __construct($data) {
$this->_array = $data;
}
public function offsetExists($offset) {
return isset($this->_array[$offset]);
}
public function offsetGet($offset) {
if (is_numeric($offset) || !isset($this->_array[$offset])) {
$values = array_values($this->_array) ;
if (!isset($values[$offset])) {
return false ;
}
return $values[$offset] ;
}
return $this->_array[$offset];
}
public function offsetSet($offset, $value) {
return $this->_array[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->_array[$offset]);
}
}
I am wondering if there isn't any built in way in PHP to do this. I'd much rather use native functions, but so far I haven't seen anything that does this.
Any ideas?
Thanks,
Fanis
how about this
$data = array(
'one' => 'something',
'two' => 'else',
'three' => 'completely'
) ;
then
$keys = array_keys($data);
Then
$key = $keys[$index_you_want];
Then
echo $data[$key];
There isn't a built-in way to do this.
If it's a one-off thing you could use something like:
$array = array_merge($array, array_values($array));
This won't update as you add new items to the array though.
i noticed you mentioned it is a readonly database result set
if you are using mysql then you could do something like this
$result = mysql_query($sql);
$data = mysql_fetch_array($result);
mysql_fetch_array returns an array with both associative and numeric keys
http://nz.php.net/manual/en/function.mysql-fetch-array.php
Sometimes it's easier to check if you have an associative key or an index with is_int()
if(is_int($key))
return current(array_slice($data, $key, 1));
else
return $data[$key];
PHP functions such as 'array_map' take a callback, which can be a simple function or a class or object method:
$array2 = array_map('myFunc', $array);
or
$array2 = array_map(array($object, 'myMethod'), $array);
But is there a syntax to pass a method which is bound within the iteration to the current object (like 'invoke' in Prototype.js)? So that the following could be used:
$array2 = array_map('myMethod', $array);
with the effect of
foreach($array as $obj) $array2[] = $obj->myMethod();
Obviously I can use this form, or I can write a wrapper function to make the call, and even do that inline. But since 'myMethod' is already a method it seems to be going round the houses to have to do one of these.
Not currently. When php 5.3 comes out, you could use the following syntax:
$array2 = array_map(function($obj) { return $obj->myMethod(); }, $array);
function obj_array_map($method, $arr_of_objects) {
$out = array();
$args = array_slice(func_get_args(), 2);
foreach ($arr_of_objects as $key => $obj) {
$out[$key] = call_user_func_array(Array($obj, $method), $args);
}
return $out;
}
// this code
$a = Array($obj1, $obj2);
obj_array_map('method', $a, 1, 2, 3);
// results in the calls:
$obj1->method(1, 2, 3);
$obj2->method(1, 2, 3);
Basically, no. There is no special syntax to make this any easier.
I can think of a fancier way of doing this in PHP 5.3, seeing as there's always more than one way to do things in PHP, but I'd say it wouldn't necessarily be better than your foreach example:
$x = array_reduce(
$array_of_objects,
function($val, $obj) { $val = array_merge($val, $obj->myMethod()); return $val; },
array()
);
Just go with your foreach :)
<?php
// $obj->$method(); works, where $method is a string containing the name of the
// real method
function array_map_obj($method, $array) {
$out = array();
foreach ($array as $key => $obj)
$out[$key] = $obj->$method();
return $out;
}
// seems to work ...
class Foo {
private $y = 0;
public function __construct($x) {
$this->y = $x;
}
public function bar() {
return $this->y*2;
}
}
$objs = array();
for ($i=0; $i<20; $i++)
$objs[] = new Foo($i);
$res = array_map_obj('bar', $objs);
var_dump($res);
?>
voila!
This is a bit of a silly answer, but you could subclass ArrayObject and use that instead of a normal array:
<?php
class ArrayTest extends ArrayObject {
public function invokeMethod() {
$result = array();
$args = func_get_args();
$method = array_shift($args);
foreach ($this as $obj) {
$result[] = call_user_func_array(array($obj, $method), $args);
}
return $result;
}
}
//example class to use
class a {
private $a;
public function __construct($a) {
$this->a = $a;
}
public function multiply($n) {
return $this->a * $n;
}
}
//use ArrayTest instance instead of array
$array = new ArrayTest();
$array[] = new a(1);
$array[] = new a(2);
$array[] = new a(3);
print_r($array->invokeMethod('multiply', 2));
Outputs this:
Array
(
[0] => 2
[1] => 4
[2] => 6
)
I would use create_function() to ... well ... create a temporary function for array_map while waiting for PHP 5.3
$func = create_function('$o', '$o->myMethod();');
array_map($func, $objects);