0

A solution to the problem raised in this question where a Client entity is split across two databases - one local, one foreign, is to build the local client such that it contains the foreign client as an object and has getters/setters to maintain the foreign client object. Local relationships are maintained in the local Client entity. The question now is how best to create the Client entity so that it can be used in more than one place.

Current Client controller method

public function getClient($id = null) {
    if (!empty($id)) {
        $localEm = $this->getDoctrine()->getManager();
        $foreignEm = $this->getDoctrine()->getManager('split');

        $client = $localEm->getRepository('ManaClientBundle:Client')->find($id);
        $foreignId = $client->getCid();
        $foreignClient = $foreignEm->getRepository('ManaSplitBundle:Client')->find($foreignId);

        $client->setForeignClient($foreignClient);
    } else {
        $client = new Client();
        $client->setForeignClient(new ForeignClient());
    }
    return $client;
}

Attempts to do this or something like it in the Client Repository fail. As do attempts to inject the following service into a standalone class. This fails with the error about argument 1 of the constructor being missing.

services.yml snippet:

      foreign_client:
        class: Mana\ClientBundle\DataCombine\ClientCombine
        arguments:
          localEm: @doctrine.orm.entity_manager
          foreignEm: @doctrine.orm.split_entity_manager

ClientCombine class snippet:

namespace Mana\ClientBundle\DataCombine;

use Doctrine\ORM\EntityManager;

class ClientCombine {

    private $localEm;
    private $foreignEm;

    public function __construct(EntityManager $localEm, EntityManager $foreignEm) {
        $this->localEm = $localEm;
        $this->foreignEm = $foreignEm;
    }
...
}
Community
  • 1
  • 1
geoB
  • 4,578
  • 5
  • 37
  • 70
  • What do you mean, 'used in more than one place' ? – Lighthart Apr 19 '13 at 05:29
  • @Lighthart: There is more than one controller that needs access to the Client entity. For example, client contacts are managed via a ContactController. Your question makes me wonder if I've overdesigned this thing. – geoB Apr 19 '13 at 13:03
  • You've been chipping away at this for awhile, I'm trying to see how it relates to your other questions... but the entity is the entity. All controllers may have access to it, provided all controllers have access to all repositories (this would be 'normal'....) – Lighthart Apr 19 '13 at 14:16
  • If I could build this hybrid client in a repository all controllers would have access. As far as I can tell, it is not possible to pass an argument to a custom repository function that will construct the entity. If it is possible I'd really like to see that. It appears to be inappropriate to use an entity manager inside an entity; doing so could solve the problem – geoB Apr 19 '13 at 14:44

1 Answers1

1

I use two entities Beverage and Beer. Each has its own entity manager. Beer is in frosty and beverage is in freezer. My strategy is to get a list of both types of entities, and pair them up, on the beverage side, and match them up. Similar functionality exists for a single entity. However, if you deal with a list of entities you need to do them in bulk, or you will have an extra query FOR EACH ENTITY ON THE LIST. This is bad.

The following has, I believe all non-essential parts stripped out. This is a painful and cumbersome approach. I was able to abandon it by going to a schema-based approach, because you can specify schema in doctrine orms. However, this code did do the job for me.

You have been warned, repeatedly, ( by me in other Q&A pairs, actually. )

A super controller, of which your controllers should extend to get the marriage functionality.

    <?php

    namespace beverage\beverageBundle\Controller;

    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;

    class ThawController extends Controller
    {
        public static function defrost( $controller, $beverages ) {
            $em = $controller->getDoctrine()->getManager( 'frosty' );
            $beverageIds = array();
            foreach ( $beverages as $k =>$v ) {
                array_push( $beverageIds, $v->getIceid() );
            }

            $qb=$em->getRepository( 'frostyfrostyBundle:Beer' )
            ->createQueryBuilder( 't' )
            ;
            if ( array() == $beverageIds ) {return null;}
            $qbeers=$qb->where( $qb->expr()
                ->in( 't.id', $beverageIds ) )
            ->getQuery()
            ->getResult();
            foreach ( $qbeers as $k => $beer ) {
                $id=$beer->getId();
                $beers[$id]=$beer;
            }
            foreach ( $beers as $k => $beer ) {
            }
            foreach ( $beverages as $k => $beverage ) {
                $beverage->ice( $beers[$beverage->getIceid()] );
            }

            return $beverages;
        }

        public static function thaw( $controller, $beverage ) {
            $beer= null;
            $em = $controller->getDoctrine()->getManager( 'frosty' );
            $beer=$em->getRepository( 'frostyfrostyBundle:Beer' )
            ->createQueryBuilder( 't' )
            ->where( 't.id = '.$beverage->getIceid() )
            ->getQuery()
            ->getSingleResult();
            $beverage->ice( $beer );
            return $beverage;
        }
    }

and entity code:

    <?php

    namespace freezer\freezerBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;
    use frosty\frostyBundle\Entity\Beer as beer;

    class Student {
        private $id;
        private $iceid;

        public function getId() {
            return $this->id;
        }

        public function setIceid( $iceid ) {
            $this->iceid = $iceid;

            return $this;
        }

        public function getIceid() {
            return $this->iceid;
        }

        public function __construct( beer $beer=null
                                   , $manyToMany = null ) {
            if ( $beer instanceof \frosty\frostyBundle\Entity\Beer ) {
                $this->ice( $beer, $manyToMany );
            }
        }

        public function setBeer( \frosty\frostyBundle\Entity\Beer $beer=null){
            $this->beer = $beer;

            return $this;
        }

        public function getBeer() {
            return $this->beer;
        }

        public function ice( snowflake $snowflake=null
                           , $manyToMany = null ) {
            if ( $snowflake instanceof 
                 \frosty\frostyBundle\Entity\Beer ) {
                $methods=get_class_methods( get_class( $snowflake ) );
                $methods=array_filter( $methods
                                     , function( $item ) use ( &$methods ) {
                                           next( $methods );
                                           if ( "__" == substr($item 0,2)) 
                                               return false;
                                           if ( "remove" == substr($item,0,6))
                                               return false;
                                           if ( "get" == substr($item,0,3)) 
                                               return false; 
                                           return true;
                                        } );

                $amethods=array_filter( $methods
                                      , function( $item ) use ( &$methods ) {
                                            next( $methods );
                                            if ( "set" == substr($item,0,3)) 
                                                return false;
                                            return true;
                                       } );

                $methods=array_filter( $methods
                                     , function( $item ) use ( &$methods ) {
                                           next( $methods );
                                           if ( "add" == substr($item,0,3)) 
                                               return false;
                                           return true;
                                       } );

                foreach ( $methods as $k => $v ) {
                    $this->{$v}( $snowflake->{str_replace( "set"
                                                         , "get"
                                                         , $v )}() );
                }

                foreach ( $amethods as $k => $v ) {
                    if ( $manyToMany ) {
                        $results=$snowflake->{str_replace( "add"
                                                         , "get"
                                                         , $v )}();
                        foreach ( $results as $key =>$value ) {
                            $this->{$v}( $value );
                        }
                    }
                }
                $this->setIceid( $beer>getId() );
            }
        }

    }
Lighthart
  • 3,648
  • 6
  • 28
  • 47
  • I am astounded at how thick I can be. While I could certainly use a cold one right now, and your code has me drooling, the answer was substantially easier. I just needed to have `ContactController extends ClientController`! And since there is a one-to-one relation between foreign and local client data there's no need to do much in the way of querying or array manipulation. +1 'cuz I'd love a frosty one right now! – geoB Apr 20 '13 at 00:50
  • You may do that, but you still will have the excessive queries problem for large lists of entities. Enjoy a cold one. – Lighthart Apr 20 '13 at 01:12