I know this question is more data structures but since I am doing it in Symfony there might be a simpler way. I have a recursive function treeBuilder() I want to call on some data to create a hierarchy. Say a database of people and I want to create a tree structure if they live with their parents. I know I am passing an array of object to the function but it needs to be an array. I am pretty sure I need to rewrite this function so that it handles the the array of object but am stumped. I am not sure how to access the elements of the array to check the parentid. I know the code below is not correct but that is where I am at now.
Controller:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyMyBundle:Org')->findAll();
var_dump($entities);
$tree=$this->treeBuilder($entities);
return array(
'entities' => $tree,
);
}
private function treeBuilder($ar, $pid=null)
{
$op=array();
foreach( $ar as $item ) {
// I know I have an array of objects
if( $item['ParentId'] == $pid ) {
$op[$item['Id']] = array(
'Street' => $item['Street'],
'ParentId' => $item['ParentId']
);
$children = self::treeBuilder( $ar, $item['Id'] );
if( $children ) {
$op[$item['Id']]['children'] = $children;
}
}
}
return $op;
}
var_dump($entities) from indexAction():
/export/www/working/symfony/src/Company/MyBundle/Controller/DepController.php:34:
array (size=60)
0 =>
object(Company\MyBundle\Entity\Org)[1556]
private 'Name' => string 'Me' (length=46)
private 'Street' => string '123 Sesame' (length=255)
private 'City' => string 'Myhometown' (length=255)
private 'ParentId' => int 0
private 'Id' => int 1
1 =>
object(Company\MyBundle\Entity\Org)[1557]
private 'Name' => string 'Me2' (length=46)
private 'Street' => string '123 Sesame' (length=255)
private 'City' => string 'Myhometown' (length=255)
private 'ParentId' => int 1
private 'Id' => int 2
If you need to get entities as arrays instead of objects, you would need to use Doctrine's hydrator:
$em = $this->getDoctrine()->getManager();
$orgRepo = $em->getRepository('CompanyMyBundle:Org');
$entities = $orgRepo->createQueryBuilder('org')
->getQuery()
->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
Note:
I would suggest to leave entities as objects and use getters:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyMyBundle:Org')->findAll();
$tree = $this->treeBuilder($entities);
return array(
'entities' => $tree,
);
}
private function treeBuilder($entities, $pid = null)
{
$op = array();
/** Org $entity */ //Type hinting, if you use autocompletion
foreach ($entities as $entity) {
if ($entity->getParentId() == $pid) {
$op[$entity->getId()] = [
'Street' => $entity->getStreet(),
'ParentId' => $entity->getParentId()
];
$children = self::treeBuilder($entities, $entity->getId());
if (!empty($children)) {
$op[$entity->geId()]['children'] = $children;
}
}
}
return $op;
}
Related
I want to get data from api request. I make a query on the value of oc52. Get $date array. The issuing server adds the MH prefix. Which generates itself relative to the name. I am trying to extract part of the array using the class.
This is the array I get when querying:
$data = [
[
'product' => 'CH C104.12',
'brand' => 'CH C104.12',
'price' => 12.34,
],
[
'product' => 'MH OC52',
'brand' => 'MH OC52',
'price' => 56.78,
],
[
'product' => 'WX WL7074-12',
'brand' => 'WX WL7074-12',
'price' => 90.12,
],
];
Here's the class I'm doing a search for
class ProductFilterIterator extends \FilterIterator
{
protected $filter;
public function __construct(\Iterator $iterator, $filter)
{
$this->filter = $filter;
parent::__construct($iterator);
}
public function accept() : bool
{
$current = $this->getInnerIterator()->current();
return $current['product'] == $this->filter;
}
}
$iterator = (new \ArrayObject($data))->getIterator();
$filter1 = new ProductFilterIterator($iterator, 'OC52');
foreach ($filter1 as $data) {
echo "<pre>";
var_dump($data);
echo "</pre>";
}
Does nothing reflect? If I write in line MH OC52:
$filter1 = new ProductFilterIterator($iterator, 'MH OC52');
Then everything works.
How do I implement it if I don't know the front - MH ???
If you just want to check the end of the string, this stores the length to check after storing the filter. Then in the main accept() method, it just looks at the last part of the string (using substr()) in the array to compare...
class ProductFilterIterator extends \FilterIterator
{
protected $filter;
protected $length;
public function __construct(\Iterator $iterator, $filter)
{
$this->filter = $filter;
$this->length = -strlen($filter);
parent::__construct($iterator);
}
public function accept() : bool
{
$current = $this->getInnerIterator()->current();
return substr($current['product'], $this->length) == $this->filter;
}
}
I have a system where I am creating multiple classes that all extend from an abstract class.
Each class also declares 'settings' for that particular class type.
Example:
class First extends Base {
protected $name = 'First';
protected $lug = 'first';
protected $fields = [
'name',
'address',
'phone',
];
function __construct()
{
parent::__construct();
}
public function abstractMethod()
{
// do stuff for this particular class
}
}
and
class Second extends Base {
protected $name = 'Second';
protected $lug = 'second-one';
protected $fields = [
'first-name',
'last-name',
'email',
];
function __construct()
{
parent::__construct();
}
public function abstractMethod()
{
// do stuff for this particular class
}
}
Now what I want to be able to do is grab all extended classes and their 'settings' and return something like this:
$classes = [
'first' => [
'name' => 'First',
'slug' => 'first',
'fields' => ['name', 'address', 'phone']
],
'second' => [
'name' => 'Second',
'slug' => 'second-one',
'fields' => ['first-name', 'last-name', 'email']
]
];
So how would I go about doing this? Is there a better way?
I am using Laravel if that helps.
Edit: To Explain why not a duplicate
I'm not just after a way to get classes and their information I am after a way to architect this situation. I am essentially creating an extensible plugin system and need a way to Tell-Don't-Ask which plugins have been added.
I didn't try it, but it should work. Or it'll directs you.
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Base'))
$result[] = get_class_vars($class);
}
But your properties needs to be public also.
What about using ReflectionClass? Getting properties is quite easy, example from manual below. Listing extended classes should be easy too.
<?php
class Bar {
protected $inheritedProperty = 'inheritedDefault';
}
class Foo extends Bar {
public $property = 'propertyDefault';
private $privateProperty = 'privatePropertyDefault';
public static $staticProperty = 'staticProperty';
public $defaultlessProperty;
}
$reflectionClass = new ReflectionClass('Foo');
var_dump($reflectionClass->getDefaultProperties());
Output:
array(5) {
["staticProperty"]=>
string(14) "staticProperty"
["property"]=>
string(15) "propertyDefault"
["privateProperty"]=>
string(22) "privatePropertyDefault"
["defaultlessProperty"]=>
NULL
["inheritedProperty"]=>
string(16) "inheritedDefault"
}
Using ReflectionObject you can do it like this:
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Base')) {
$obj = new $class;
$refObj = new ReflectionObject($obj);
$props = $refObj->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
$classProps = array();
foreach ($props as $prop) {
$property = $refObj->getProperty($prop->getName());
$property->setAccessible(true);
$classProps[$prop->getName()] = $property->getValue($obj);
}
$result[$class] = $classProps;
}
}
print_r($result);
Output:
Array (
[First] => Array (
[name] => First
[lug] => first
[fields] => Array (
[0] => name
[1] => address
[2] => phone
)
)
[Second] => Array (
[name] => Second
[lug] => second-one
[fields] => Array (
[0] => first-name
[1] => last-name
[2] => email
)
)
)
i'm trying to refresh my memory of OO & array structure. i have,
class room{
private $people = array(
'name' => array(
'height' => null,
'age' => null
)
);
function set($list){
foreach($list as $person){
$this->people[$person['name']]['height'] = $person['height'];
$this->people[$person['name']]['age'] = $person['age'];
}
}
function print(){
foreach($this->people as $k => $v){
echo $k . "<br>";
echo $v['height'] . ":" . $v['age'] . "<br><br>";
}
}
}
$input = array( array('name' => 'John', 'height' => '6.4', 'age' => '20'),
array('name' => 'Jane', 'height' => '5.2', 'age' => '21')
);
$i = new room;
$i->set($input);
$i->print();
the output is,
name
:
John
6.4:20
Jane
5.2:21
i'm confused as why name : appears first, when the input array only contains 2 values of each person. i am unsure if i am using my arrays correctly, could someone point out my mistake?
My overall aim of this is to have correct understanding of arrays within arrays & how to best set & get the values
It's because you've initialised the $people array to contain those values
private $people = array(
'name' => array(
'height' => null,
'age' => null
)
);
Change it to:
private $people = array();
that's the good way to do it
your people class
class people {
//properties
private $name;
private $height;
private $age;
//setters
public function setName($name) {
$this->name = $name;
}
public function setHeight($height) {
$this->height = $height;
}
public function setAge($age) {
$this->age = $age;
}
//getters
public function getName() {
return $this->name;
}
public function getHeight() {
return $this->height;
}
public function getAge() {
return $this->age;
}
}
your room class
class room {
//properties
private $people = array();
//setters
public function setPeople($people) {
$this->people[] = $people;
}
//getters
public function getPeoples() {
return $this->people;
}
}
and how to control it in OOP
$people1 = new people();
$people1->setName('John');
$people1->setHeight('6.4');
$people1->setAge('20');
$people2 = new people();
$people2->setName('Jane');
$people2->setHeight('5.2');
$people2->setAge('21');
$room = new room();
$room->setPeople($people1);
$room->setPeople($people2);
// Removing people array initial data will solve the issue :)
class room{
private $people = array();
function set($list){
foreach($list as $person){
$this->people[$person['name']]['height'] = $person['height'];
$this->people[$person['name']]['age'] = $person['age'];
}
}
function print(){
foreach($this->people as $k => $v){
echo $k . "<br>";
echo $v['height'] . ":" . $v['age'] . "<br><br>";
}
}
}
$input = array( array('name' => 'John', 'height' => '6.4', 'age' => '20'),
array('name' => 'Jane', 'height' => '5.2', 'age' => '21')
);
$i = new room;
$i->set($input);
$i->print();
I have a PHP class I would like to transform in a JSON on several levels like this type:
{"interface":{"Version":"0"},"Container":[{"id":"1","Element":[{"text":"Test","id":"0"},{"text":"Toto","id":"1"}]}]}
In my PHP class I have a function who returns the JSON of my private attributes who are arrays:
return (json_encode((get_object_vars($this)), JSON_UNESCAPED_UNICODE));
Private attributes of my class:
private $interface = '';
private $Container = array(array('id' => '1'));
private $Element = array('text' => 'Test', 'id' => '0');
Do you know how I could have a JSON like above ?
In pleasure to read you.
Not sure about your class members but as long as they are accessible you can generate the JSON string. Below if the example of this
$Contenant = array(array('id' => '1'));
$Element = array('text' => 'Test', 'id' => '0');
$json = json_encode(array('inerrface'=>array(
'content'=>$Contenant,
"Element"=>$Element
)
)
);
echo $json ;
You could implement the IteratorAggregate interface like in the following example
class YourClass implements IteratorAggregate {
protected $member1 = array();
protected $member2 = array();
...
public function getIterator() {
$tmpArr = array();
// create the structure you want in $tmpArr
return new ArrayIterator($tmpArr);
}
}
$myClass = new MyClass();
$iterator = $myClass->getIterator();
$encodedData = json_encode($iterator);
As of PHP5.4 you have the JsonSerializable interface ready to use. With this interface, you cann use direct modifications like in the example given in: http://de2.php.net/manual/en/jsonserializable.jsonserialize.php
have fun! ;)
This will get you started, take a look at the constructor what data input requires. You could make a private function doing the same thing, assigning your values to the structure and then printing it:
class Test{
private $data;
public function __construct($version = 0, $records = array()){
$data['interface'] = array('Version' => $version);
$data['Container'] = array();
for ($i = 0; $i < count($records); $i++) {
$data['Container'][$i] = $records[$i];
}
// your test input
print_r(json_decode('{"interface":{"Version":"0"},"Container":[{"id":"1","Element":[{"text":"Test","id":"0"},{"text":"Toto","id":"1"}]}]}',true));
// actual input
print_r($data);
// printing our actual data as json string
echo json_encode($data);
}
public function __destruct(){
}
}
$element1 = array('text' => 'Test', 'id' => 0);
$element2 = array('text' => 'Toto', 'id' => 1);
$elements = array($element1, $element2);
$record = array('id' => 1, 'element' => $elements);
$records = array($record);
new Test(0, $records);
You need to structure it, working example: example
class test {
private $interface = '';
private $Contenant = array(array('id' => '1'));
//private $Element = array(array('text' => 'Test', 'id' => '0'));
public function json(){
$this->Contenant = array(
array('id' => '1',
'Element' => array(array('text' => 'Test', 'id' => '0'))
),
);
return json_encode((get_object_vars($this)), JSON_UNESCAPED_UNICODE);
}
}
$t = new test();
$encode = $t->json();
echo $encode;
OUTPUT
{"interface":"","Contenant":[{"id":"1","Element":[{"text":"Test","id":"0"}]}]}
Given that you have an array that contains varying amounts of objects, how can you access the properties of the last object? I tried to use end($array); but it gives the error: Object of class Post could not be converted to string
The class is:
class Post {
private $datafile;
public $index;
public $subIndex;
public $poster;
public $title;
public $message;
public $date;
// constructor and some unrelated methods here
public function getCommentData($givenIndex) {
$comments = null;
$data = file($this->datafile);
foreach($data as $row) {
list($index, $subIndex, $poster, $message, $date) = explode('|', $row);
$this->index = $subIndex; // SubIndex ties a Comment to a Post (same ID)
if($this->index == $givenIndex) {
$comment = new Post();
$comment->poster = $poster;
$comment->message = $message;
$comment->date = date(DATEFORMAT, strtotime($date));
$comments[] = $comment;
}
}
return $comments;
}
}
Now, I would like to access only the last Comment item's properties, but I'm not sure how it should be done? In a regular array, end() is quick and easy to use, but how about with objects as it doesn't seem to work?
Here is an example var_dump:
array (size=2)
0 =>
object(Post)[4]
private 'datafile' => null
public 'index' => null
public 'subIndex' => null
public 'poster' => string 'Postaaja' (length=8)
public 'title' => null
public 'message' => string 'Kommentti' (length=9)
public 'date' => string '5 Mar 2013 | 23:12' (length=18)
1 =>
object(Post)[5]
private 'datafile' => null
public 'index' => null
public 'subIndex' => null
public 'poster' => string 'Toinenkin' (length=9)
public 'title' => null
public 'message' => string 'Lisäkommentti' (length=14)
public 'date' => string '5 Mar 2013 | 23:13' (length=18)
Thanks!
EDIT:
Here's the way I tried to use it:
$comments = new Post(FILECOMMENTS);
$currentComments = $comments->getCommentData($i); // $i is the index of current newspost item
$newsComments = new Format();
$newsComments->formatShortComment($currentComments, $i);
And the method in the Format class:
// This comment is displayed as a short text in the main News view
public function formatShortComment($data, $index) {?>
<div class="newsComments">
<p class="newsPreviewComment">
<?php
$lastItem = end($data);
if(!empty($lastItem->message)) {
echo '<i>"',$lastItem->message,'"</i> ';
echo '-',$lastItem->poster;
}
?></p>
» Show All/Add comments
(<?php echo $commentCount; ?>)
</div><?php
}
You could try:
$tempArray = array_values($data);
$lastItem = $tempArray[count($tempArray)-1];
I hope I didn't miss something important, but if you're just trying to get the last element of a PHP array:
$lastItem = $data[count($data) - 1];