1

I cant get my head around how to access properties over 2 relationships with the neo4j-php-ogm library.

Say for example I have a "user" node, which connects to MANY "resource" nodes, which in return are each connected to a fixed number of predefined "MetaResource" node. The "resource" nodes have properties and the "MetaResource" nodes have the Meta-Properties of each resource type. How can I know access the properties of the "MetaResource" nodes starting from a "user" node? In Neo4j one such route looks like this:

(user)-[r:HAS_RESOURCE]->(resource)-[m:METARESOURCE]->(metaResource)

My example user entity:

/**
 * @OGM\Node(label="User")
 */

class User
{
    /**
     * @OGM\GraphId()
     * @var int
     */
    private $id;

    /**
     * @OGM\Relationship(type="HAS_RESOURCE", direction="OUTGOING", targetEntity="\AppBundle\Entity\Resources", collection=true)
     * @var ArrayCollection|\AppBundle\Entity\Resources[]
     */
    protected $resources;

    /**
     * @return \Doctrine\Common\Collections\ArrayCollection|\AppBundle\Entity\Resources[]
     */
    public function getResources()
    {        
        return $this->resources;
    }

    /**
     * @return \Doctrine\Common\Collections\ArrayCollection|\AppBundle\Entity\Resources[]
     */
    public function getResource($name)
    {

        foreach($this->resources as $resource){
            if($resource->getResourceType() == $name){
                return $resource;
                break;
            }
        }        
    }


    /**
     * @param AppBundle\Entity\Resources $resources
     */
    public function addResource(Resources $resources)
    {
        if (!$this->resources->contains($resources)) {
            $this->resources->add($resources);
        }
    }
}

My Example Resource Entity:

/**
 * @OGM\Node(label="Resources")
 */

class Resources
{
    /**
     * @OGM\GraphId()
     * @var int
     */
    protected $id;

    /**
     * @OGM\Property(type="int")
     * @var int
     */

    protected $resourcecount;    


   /**
    * @OGM\Relationship(type="METARESOURCE", direction="OUTGOING", targetEntity="\AppBundle\Entity\MetaResource", collection=false)
    * @var MetaResource
    */

    protected $metaResource;

    /**
     * @param \AppBundle\Entity\MetaResource $metaResource
     */

    public function __construct(MetaResource $metaResource)
    {
        $this->metaResource = $metaResource;
    }    

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

    public function getResourceCount()
    {
        return $this->resourcecount;
    }

    public function setResourceCount($resourcecount)
    {
        $this->resourcecount = $resourcecount;
    }

    /**
     * @return \AppBundle\Entity\MetaResource
     */

    public function getMetaResource()
    {        
        return $this->metaResource;
    }


}

And my Example MetaResource Entity:

/**
 * @OGM\Node(label="MetaResource")
 */

class MetaResource
{
    /**
     * @OGM\GraphId()
     * @var int
     */
    protected $id;

    /**
     * @OGM\Property(type="string")
     * @var string
     */

    protected $resourceType;

    /**
     * @OGM\Property(type="string")
     * @var string
     */

    protected $name_DE;    

    /**
     * @OGM\Property(type="string")
     * @var string
     */

    protected $icon;

    /**
     * @OGM\Property(type="string")
     * @var string
     */

    protected $iconColour;

    /**
     * @OGM\Property(type="string")
     * @var string
     */

    protected $colour;  

    public function getResourceType()
    {
        return $this->resourceType;
    }


    public function getName_DE()
    {
        return $this->name_DE;
    }


    public function getIcon()
    {
        return $this->icon;
    }


    public function getIconColour()
    {
        return $this->iconColour;
    }

    public function getColour()
    {
        return $this->colour;
    }

}

And finally the code from my controller, which sets up the relations:

$metaResource=$em->getRepository(MetaResource::class)->findOneBy('resourceType', 'wood');  
$rWood = new Resources($metaResource);            
$rWood->setResourceCount(20);
$em->persist($rWood);
$em->flush();
$user->addResource($rWood);

If I now have a look at the Neo4j webconsole, all relationships and nodes are correctly inserted.

Now, if I get the resources of a user with $user->getResources() I successfully get all the resource objects, but the "$metaResource" property is always NULL instead of the anticipated Object of my MetaResource entity. For example if I do:

foreach($user->getResources() as $resource){
    var_dump($resource->getMetaResource());        
}

Then it outputs only NULLs.

On the other hand, if I directly get a resource object (for example with $resource = $em->getRepository(Resources::class)->findOneById(123) ) and then try to get the connected MetaResource with $resource->getMetaResource() it works. What am I missing?

Cheers

Mfbaer
  • 465
  • 3
  • 15
  • Hey @Joran I will check that normally it should be handled with the latest beta and the Lazy annotation, will try your use case – Christophe Willemsen Jul 21 '16 at 08:30
  • Hi Chris :) I have solved my specific case by using a relationship entity and storing the properties on the relationship. So instead of having: `(user)-[r:HAS_RESOURCE]->(resource{property1, property2})-[m:METARESOURCE]->(metaresource{property1, property2})` I now have: `(user)-[r:HAS_RESOURCE{property1, property2}]->(metaresource{property1, property2})`. This works fine for this specific use case. But it would be great to know how to get data over multiple relationships! Im my case, how and where would I have to define lazy loading? Cheers – Mfbaer Jul 21 '16 at 11:37
  • I added a small section at the end of the docs, but normally your use case should work, can be buggy though, I ll take yr example as test case and reply here when working – Christophe Willemsen Jul 21 '16 at 11:43
  • Great thanks! Tell me if I can help in any way! – Mfbaer Jul 21 '16 at 11:43
  • Hi Chris. I just ran into the same error when using the relationship entity. But only if I do not want a collection but a single object/node to be stored. I think the error is in my annotations? How should I annotate if I only want a single object to be stored? Is this the right way: ` /** * @OGM\Relationship(relationshipEntity="\AppBundle\Entity\UserTeam", type="IN_TEAM", direction="OUTGOING", collection=false) * @var \AppBundle\Entity\UserTeam */ protected $team; ` – Mfbaer Jul 21 '16 at 20:35
  • Yes the support to this is for collections only, this will be done for single objects though. I will be on the computer tomorrow (belgium national day today and am out for the fireworks ) . Thanks for trying ! – Christophe Willemsen Jul 21 '16 at 20:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/117970/discussion-between-joran-and-christophe-willemsen). – Mfbaer Jul 21 '16 at 20:40
  • After further testing I can confirm that the problem seems to be that collections are needed (`collection=true` in the annotation). If only one object should be stored this can be circumnavigated by only returning the first object (as there should only always be one object anyway): `return $this->relationshipCollection->first();`. – Mfbaer Jul 22 '16 at 11:03
  • Right. So this is the next step. Actually only collections are proxied by using a relationshipFinderAware collection instead of a normal collection, next is to create a proxy of inversed relationship references for non-collection relationships with an initializer when you access the object. I'm working on it right now. This is needed because you want a class of a certain type and not a collection back – Christophe Willemsen Jul 22 '16 at 11:20
  • any chance you can join the neo4j slack channel ? – Christophe Willemsen Jul 22 '16 at 11:27

1 Answers1

1

I have made some progress regarding this use case. I'm using now a proxy generator that can handle this use case (this was a big missing part into the lib actually but takes time to implement).

So please test with the latest release 1.0.0-beta7.

Christophe Willemsen
  • 19,399
  • 2
  • 29
  • 36