21

Wants to convert doctrine entiry object to normal array, this is my code so far,

 $demo = $this->doctrine->em->find('Entity\User',2);

Getting entity object ,

Entity\User Object
(
[id:Entity\User:private] => 2
[username:Entity\User:private] => TestUser
[password:Entity\User:private] => 950715f3f83e20ee154995cd5a89ac75
[email:Entity\User:private] => [email protected]
[firm_id:Entity\User:private] => Entity\Firm Object
    (
        [id:Entity\Firm:private] => 16
        [company_name:Entity\Firm:private] => TestFirm
        [company_detail:Entity\Firm:private] => India
        [created_at:Entity\Firm:private] => DateTime Object
            (
                [date] => 2014-08-01 18:16:08
                [timezone_type] => 3
                [timezone] => Europe/Paris
            )

        [user:Entity\Firm:private] => 
    )

[created_at:Entity\User:private] => DateTime Object
    (
        [date] => 2014-08-01 15:12:36
        [timezone_type] => 3
        [timezone] => Europe/Paris
    )

[updated_at:Entity\User:private] => DateTime Object
    (
        [date] => 2014-08-01 15:12:36
        [timezone_type] => 3
        [timezone] => Europe/Paris
    )

[firm:protected] => 
) ,

Tried this ,But as per my requiremnet do not want to user doctrine_query. Thanks.

3
  • 1
    try this var_dump(get_object_vars(object)); ? Commented Aug 6, 2014 at 10:57
  • Its not working! thanks btw :) Commented Aug 6, 2014 at 11:01
  • iswinky's solution would only work if your members are declared public. Since they are private, it can't access them. You could however add a function to your entity that returns get_object_vars($this); and call that from the outside. Commented Jul 25, 2018 at 18:43

13 Answers 13

26

You can try something like this,

    $result = $this->em->createQueryBuilder();
    $app_code = $result->select('p')
            ->from('YourUserBundle:User', 'p')
            ->where('p.id= :id')
            ->setParameter('id', 2)
            ->getQuery()
            ->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);

Another way,

 $this->em->getRepository('YourUserBundle:User')
      ->findBy(array('id'=>1));

Above will return an array but contains doctrine objects. Best way to return an array is using the doctrine query.

Hope this helps. Cheers!

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks but i've mentioned already ,do not want to use doctrine query thing.
Oh. Sorry about that. Aren't you not allowed to use entity repository?
Actually yes, can you provide me an example?
That means no method to convert entity object to array?
Yeah that's strange. So far I haven't found such. I use doctrine query builder and hydrate the results to an array when I have such a requirement. Cheers.
|
9

Note: If your reason for wanting an array representation of an entity is to convert it to JSON for an AJAX response, I recommend checking this Q&A: How to encode Doctrine entities to JSON in Symfony 2.0 AJAX application?. I particularly like the one about using the built-in JsonSerializable interface which is similar to my answer.


Since Doctrine does not provide a way to convert entities to associative arrays, you would have to do it yourself. One easy way is to create a base class that exposes a function that returns an array representation of the entity. This could be accomplished by having the base class function call get_object_vars on itself. This functions gets the accessible properties of the passed-in object and returns them as an associative array. Then you would simply have to extend this base class whenever you create an entity that you would want to convert to an array.

Here is a very simple example:

abstract class ArrayExpressible {
    public function toArray() {
        return get_object_vars($this);
    }
}

/** @Entity */
class User extends ArrayExpressible {

    /** @Id @Column(type="integer") @GeneratedValue */
    protected $id = 1; // initialized to 1 for testing

    /** @Column(type="string") */
    protected $username = 'abc';

    /** @Column(type="string") */
    protected $password = '123';

}

$user = new User();
print_r($user->toArray());
// Outputs: Array ( [id] => 1 [username] => abc [password] => 123 )

Note: You must make the entity's properties protected so the base class can access them using get_object_vars()


If for some reason you cannot extend from a base class (perhaps because you already extend a base class), you could at least create an interface and make sure your entities implement the interface. Then you will have to implement the toArray function inside each entity.

Example:

interface ArrayExpressible {
    public function toArray();
}

/** @Entity */
class User extends SomeBaseClass implements ArrayExpressible {

    /** @Id @Column(type="integer") @GeneratedValue */
    protected $id = 1; // initialized to 1 for testing

    /** @Column(type="string") */
    protected $username = 'abc';

    /** @Column(type="string") */
    protected $password = '123';

    public function toArray() {
        return get_object_vars($this);
        // alternatively, you could do:
        // return ['username' => $this->username, 'password' => '****']
    }

}

$user = new User;
print_r($user->toArray());
// Outputs: Array ( [id] => 1 [username] => abc [password] => 123 )

Comments

3

If you alread have the object entity fetched form the database, you could also work with the DoctrineModule\Stdlib\Hydrator\DoctrineObject.

/**
 * Assume your entity for which you want to create an array is in $entityObject.
 * And it is an instance of YourEntity::class.
 */
$tmpObject = new DoctrineObject($this->entityManager, YourEntity::class);
$data = $tmpObject->extract($entityObject);

Now $data will contain your object as array.

P.S. I'm not sure this was possible when the question was asked.

Comments

2

I'm new to Symfony, but there is some working (but strange) way:

json_decode($this->container->get('serializer')->serialize($entity, 'json'))

4 Comments

Be carefull cause this way you can get a nasty recursion in case the serializer tries to get data from it's entity relations
Thank you @le0diaz, I'm using exclusions jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies to avoid some recursions
I was having the same issue and end up using exclusions too. I need it to use the @MaxDepth(n) annotation, but it doesn't work out of the box. I had to update my controller (inheriting from FOSRestController) to use something like this maybe it hels anyone too: $view = $this->view($facility); $view->getSerializationContext()->enableMaxDepthChecks(); return $this->handleView($view);
you can use this syntax : $this->container->get('serializer')->serialize($entity, 'array') you gain a jsonencode/jsondecode pass
2

I needed a toArray() method that could work after hydration but the get_object_vars() trick did not work because of the lazy loading/proxy stuff in doctrine 2.x

so here is my dropin method

use Doctrine\Common\Inflector\Inflector;
...
public function toArray() {
    $methods = get_class_methods($this);
    $array = [];
    foreach ($methods as $methodName) {
        // remove methods with arguments
        $method = new \ReflectionMethod(static::class, $methodName);
        if ($method->getNumberOfParameters() > 0) continue;
        $matches = null;
        if (preg_match('/^get(.+)$/', $methodName, $matches)) {
            // beautify array keys
            $key = Inflector::tableize($matches[1]);
            // filter unwanted data
            if (in_array($key, ['object1', 'object2'])) continue;
            $array[$key] = call_user_func([$this, $methodName]);
        }
    }
    return $array;
}

feel free to improve it

1 Comment

I improved it by adding property_exists() check inside preg_match check: if (!property_exists($this, $inflector->camelize($matches[1]))) continue;
1

I made a recursive function in my Repository a few months ago, it's not perfect (like, if you have a field createdBy and updatedBy, it will only retrieve the value for one user because of a rather simple protection against recursivity with $aClassNamesDone), but it may help:

    public function entityToArray($entity, &$aClassNamesDone=array(), $latestClassName="") {

    $result = array();

    if(is_null($entity)) {
        return $result;
    }

    $className = get_class($entity);

    // init with calling entity
    if(empty($aClassNamesDone)) {
        $aClassNamesDone[] =$className;
    }

    $uow = $this->getEntityManager()->getUnitOfWork();

    $entityPersister = $uow->getEntityPersister($className);
    $classMetadata = $entityPersister->getClassMetadata();

    //DEPENDS ON DOCTRINE VERSION
    //if(strstr($className, 'DoctrineProxies\\__CG__\\')){
    if(strstr($className, 'Proxies\\__CG__\\')){
        $uow->initializeObject($entity);
    }

    foreach ($uow->getOriginalEntityData($entity) as $field => $value) {

        if (isset($classMetadata->associationMappings[$field])) {

            $assoc = $classMetadata->associationMappings[$field];

            if (isset($classMetadata->columnNames[$field])) {
                $columnName = $classMetadata->columnNames[$field];
                $result[$columnName] = $value;
            }

            // to avoid recursivity we can look for the owning side (gives similar results as Query::HYDRATE_ARRAY):
            // elseif($assoc['isOwningSide']) { ...
            // or we can track entities explored and avoid any duplicates (this will however ignore some fields pointing to the same entity class)
            // for example: only one of createdBy, updatedBy will be kept

            else if(!in_array($assoc['targetEntity'], $aClassNamesDone) || $assoc['targetEntity'] == $latestClassName) {

                try {

                    if ($assoc['targetEntity'] != 'Timestamp') {

                        $aClassNamesDone[] = $assoc['targetEntity'];

                        $targetClass = $this->getEntityManager()->getClassMetadata($assoc['targetEntity']);

                        if (($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::MANY_TO_MANY) || ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY)) {

                            $getterName = 'get' . ucfirst($assoc['fieldName']);
                            $entityChildren = $entity->$getterName();
                            foreach ($entityChildren as $oneChild) {
                                $result[$assoc['fieldName']][] = $this->getEntityManager()->getRepository($assoc['targetEntity'])->entityToArray($oneChild, $aClassNamesDone, $assoc['targetEntity']);
                            }

                        } else if (($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_ONE) || ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::MANY_TO_ONE)) {

                            $getterName = 'get' . ucfirst($assoc['fieldName']);
                            $entityChild = $entity->$getterName();
                            $result[$assoc['fieldName']] = $this->getEntityManager()->getRepository($assoc['targetEntity'])->entityToArray($entityChild, $aClassNamesDone, $assoc['targetEntity']);

                        }
                    }

                } catch (\Exception $e) {
                    //var_dump('No entityToArray for ' . $assoc['targetEntity']);
                    throw ($e);
                }
            }

        }
    }

    return $result;
}

Comments

1

If you just need to access a single value, you can also do this...

If 'personType' were an object and you wanted the value of the relationship...

$personTypeId = $form->get('personType')->getViewData();

Comments

1

If any one want to do it using Doctrine 2, do it with UnitOfWork API. This is the only way to do it using doctrine public API.

Example:-

$em = $this->getEntityManager();
$uow = $em->getUnitOfWork();

$entity = $this->find($id);

// Returning the fetched data as an array
$uow->getOriginalEntityData($entity); // ['name' => 'Old Name', 'username'=> 'oldone']

// But it will not be synchronized with the entity
$entity->setName('New Name');
$uow->getOriginalEntityData($entity); // ['name' => 'Old Name', 'username'=> 'oldone']

// Luckily, there is a way to get changed data after called persist
$em->persist($entity);
$uow->getOriginalEntityData($entity); // ['name' => 'Old Name', 'username'=> 'oldone']
$uow->getEntityChangeSet($entity); // ['name' => ['Old Name', 'New Name']]

// Original data was syncronized after called flush method
$em->flush();
$uow->getOriginalEntityData($entity); // ['name' => 'New Name', 'username'=> 'oldone']

Read my blog post for more information. https://whizsid.github.io/blog/25/converting-a-doctrine-2-entity-to-an-array.html

Comments

0

It's work for me

       $sql="
            SELECT * FROM users
        ";
        $stmt = $this->em->getConnection()->prepare($sql);
        $users =  $stmt->executeQuery()->fetchAllAssociative();

Comments

0

A little old now, but you can also create a simple DataModel which your entities can extend from, then create a __toArray() method.

You can't use get_object_vars() because that only picks up publicly-scoped properties, and they should all be private as per standards, however your methods should be public so use those.

Something quick (clean up as needed, also not fully tested):

<?php
declare(strict_types=1);

namespace App\Entity;

use Exception;

/**
 *
 */
class DataModel
{
    /**
     * @return array
     */
    public function __toArray(): array {
        $properties = [];
        $public_methods = get_class_methods($this);
        foreach ($public_methods as $method) {
            if (str_starts_with($method, 'get')) {
                $property = lcfirst(str_replace('get', '', $method));
                try {
                    $properties[$property] = $this->$method();
                } catch (Exception $exception) {
                    //pass
                }
            }
        }
        return $properties;
    }
}

Comments

0

For what it worth, I have the same needs and struggle using the serializer solution with the MaxDepth thing, so, inspired from other posts here, that's my solution: In each entity class, add a method to get the properties:

protected function getObjectVars(): array
{
    return get_object_vars($this);
}

And in the AbstractEntity all my entities extend from, add this method:

public function toArray(): array
{
    $objectVar = [];

    if (method_exists($this, 'getObjectVars')) {
        $objectVar = $this->getObjectVars();
        foreach ($objectVar as &$item) {
            if ($item instanceof \DateTime) {
                $item = $item->format('Y-m-d H:i:s');
            }
            if ($item instanceof PersistentCollection) {
                $temp = [];
                foreach ($item as $value) {
                    $temp[] = $value->getId();
                }
                $item = $temp;
            }
            if ($item instanceof self) {
                $item = $item->getId();
            }
        }
    }

    return $objectVar;
}

Hope it will help ;)

Comments

0
function toArray(object $entity, int $depth = 0): array
{
    return ($self = function (mixed $value, int $depth, int $level = 0) use (&$self): mixed {
        return match (true) {
            $value instanceof DateTimeInterface => $value->format($value::ATOM),
            $value instanceof BackedEnum => $value->value,
            is_array($value) || $value instanceof ArrayAccess => $level >= $depth ? '{...}' : array_map(fn (mixed $item): mixed => $self($item, $depth, $level), iterator_to_array($value)),
            is_string($value) || (is_object($value) && in_array('__toString', get_class_methods($value))) => json_decode($value = (string) $value) ?: $value,
            is_object($value) => $level >= $depth ? '{...}' : array_filter(array_combine(
                array_map(fn (ReflectionProperty $property): string => $property->name, $properties = (new ReflectionClass($value))->getProperties()),
                array_map(fn (ReflectionProperty $property): mixed => $property->isInitialized($value) ? $self($property->getValue($value), $depth, $level + 1) : '{{_UNDEFINED}}', $properties)
            ), fn (mixed $value): bool => $value != '{{_UNDEFINED}}'),
            default => $value
        };
    })($entity, max($depth ?: 10, 1));
}

Comments

-12

Simply u can use this

$demo=array($demo);

2 Comments

Can you please explain, how this solves the problem. If i understand your code right, you just put $demo in an array. But the question was rather, how to transform to entity to an array.
normaly it needs the cast like this : $demo = (array)$demo; But whether is it working or not in doctrine objects , I dont know.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.