Introducing the Neo4j Symfony Bundle

Share this article

Introducing the Neo4j Symfony Bundle

This article was peer reviewed by Wern Ancheta and Christophe Willemsen. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!


Why Graphs?

There is no such thing as disconnected information, no matter where you look – people, events, places, things, documents, applications and the information about them is all heavily connected. As the volume of data grows, so does the number and dynamicity of its connections. And if you’ve tried in the past to store and query that highly connected, semi-structured data in any database, you probably experienced a lot of challenges.

Example graph

The Labeled Property Graph in a native Graph Database

Neo4j was built to handle exactly this real-world information without compromising on the number and types of connections you can have relating your entities. It is an open-source NoSQL database that uses the Labeled Property Graph to store the entities of your domain model (diagram) as Nodes and their connections as Relationships each of which can have arbitrary properties.

Nodes and releationships with properties

Neo4j is not just a graph layer on top of another database but a full blown, transactional (ACID) database with everything from managing pages of records on disk to providing a scalable, secure cluster. And as a native graph database it uses dedicated data structures to store and query highly connected data efficiently. Unlike in other databases, (complex) JOIN queries are not computed repeatedly at query time. Instead, relationships between entities are stored directly. During querying the database engine can use direct record-pointers for constant time lookups.

The open Cypher Query Language

This doesn’t just extend to modeling or storage, even the Cypher graph query language that comes with Neo4j is focused around graph patterns, this time represented as ASCII-art in your query: (dan:Person {name:"Dan"})-[:LOVES]->(ann:Person {name:"Ann"}), which make your queries extremely readable even for non-developers, e.g. here is a recommendation query (“customers like you also bought this”):

MATCH (c:Customer)-[:BOUGHT]->(:Product)<-[:BOUGHT]-(o:Customer)-[:BOUGHT]->(reco:Product)
WHERE c.id = 123 AND NOT (c)-[:BOUGHT]->(reco)
RETURN reco.name, count(*) as frequency
ORDER BY frequency DESC LIMIT 10;

Symfony, a rapid development framework for PHP

Symfony is the role model of frameworks for modern PHP. The framework has a component approach and has been around for the last 11 years. From the Symfony ecosystem we’ve seen projects like Composer, Twig, Swiftmailer, Assetic, Monolog and many more. Thanks to the component approach, it has been made easy for other projects and frameworks to reuse code from Symfony. Projects like Laravel, Silex, Sylius, Drupal, phpBB, eZ are all using Symfony components.

A key factor of Symfony’s success is the flexibility of the framework in combination with the ease of use. The standard edition of Symfony comes with Doctrine as default database layer abstraction which supports some major databases like MySQL and MongoDB. But neither the database layer nor Doctrine is a primary citizen in Symfony. They could easily be replaced by something else.

Introducing Symfony Neo4j Bundle

To provide a smooth integration between Neo4j and Symfony we’ve created the SymfonyNeo4jBundle. It wraps the excellent PHP community client by Graphaware and creates a solid Symfony experience. Thanks to the WebProfiler integration, you will see all your database calls, all the queries and their results. You will even have a view over any exceptions that are thrown when interacting with the database. You will also get detailed statistics about each database call. This makes debugging your application way easier.

The bundle also integrates the client events with the Symfony event dispatcher. You could now create event subscribers that listen to the interactions with Neo4j e.g. for integration with MonologBundle to log all your database queries.

The bundle is not opinionated in how you are using Neo4j. Using the OGM is optional. Advanced Neo4j users will have full control over the client and what Cypher gets executed.

An API like Doctrine

For developers who are familiar with Doctrine, they will know how to use GraphAware’s OGM (Object Graph Mapper). The OGM has an EntityManager that implements Doctrine’s ObjectManager interface, which gives you functions like “find”, “remove”, “persist” and “flush”. Developers will have the exact same experience working with models from Neo4j’s OGM compared to Doctrine’s MySQL EntityManagers.

Configuration

Like in most modern frameworks, Symfony separates configuration from the code. This is good software practice which the Neo4jBundle adheres to. It provides the ability to easily configure multiple connections, multiple clients and multiple entity managers. For each connection you can decide if you want to use HTTP or the new binary “bolt” protocol.

Thanks to Symfony’s configuration component, you may use Yaml, XML or PHP to specify your configuration. The default configuration will set up one connection to 127.0.0.1:7474 with the defaults username / password.

Getting started with the Symfony-Neo4j-Bundle

After installing the the bundle you may start using the clients

$client = $this->get('neo4j.client');
$results = $client->run('MATCH (n:Movie) RETURN n LIMIT 5');
foreach ($results->records() as $record) {
   $node = $record->get('n');
   echo $node->get('title'); // "The Matrix"
}

If you are used to Doctrine you may want to use the OGM. You need to add annotations to your models so the OGM will understand and map the properties properly.

use GraphAware\Neo4j\OGM\Annotations as OGM;

/**
 * @OGM\Node(label="User")
 */
class User
{
    /** @OGM\GraphId() */
    protected $id;

    /** @OGM\Property(type="string") */
    protected $name;

    /** @OGM\Property(type="int") */
    protected $age;

    // Getters and setters
}
$em = $this->get('neo4j.entity_manager');

$bart = new User('Bart Johnson', 33);
$em->persist($bart);
$em->flush();

// Retrieve from database
$john = $em->getRepository(User::class)->findOneBy('name', 'John Doe');
echo $john->getAge();

Here is an example of what the profiler may look like:

Example of the Symfony profiler page

Relationship and relationship entities

The major difference from Doctrine and MySQL is that relationships are first class citizens in Neo4j. They are as important as the nodes themselves. To create a simple relationship between a person and a movie you would use the @OGM\Relationship annotation.

use GraphAware\Neo4j\OGM\Annotations as OGM;
use GraphAware\Neo4j\OGM\Common\Collection;

/**
*
* @OGM\Node(label="Person")
*/
class Person
{
   /**
    * @var int
    *
    * @OGM\GraphId()
    */
   protected $id;

   // other code

   /**
    * @var Movie[]|Collection
    *
    * @OGM\Relationship(type="ACTED_IN", direction="OUTGOING", collection=true, mappedBy="actors", targetEntity="Movie")
    */
   protected $movies;

   public function __construct()
   {
       $this->movies = new Collection();
   }

   // other code

   /**
    * @return Movie[]|Collection
    */
   public function getMovies()
   {
       return $this->movies;
   }
}

The Movie class has an INCOMING relationship from Person.

use GraphAware\Neo4j\OGM\Annotations as OGM;
use GraphAware\Neo4j\OGM\Common\Collection;

/**
*
* @OGM\Node(label="Movie")
*/
class Movie
{
   /**
    * @var int
    *
    * @OGM\GraphId()
    */
   protected $id;

   // other code

   /**
    * @var Person[]|Collection
    *
    * @OGM\Relationship(type="ACTED_IN", direction="INCOMING", collection=true, mappedBy="movies", targetEntity="Person")
    */
   protected $actors;

   public function __construct()
   {
       $this->actors = new Collection();
   }

   // other code

   /**
    * @return Person[]|Collection
    */
   public function getActors()
   {
       return $this->actors;
   }
}

The relationship itself can have also properties. Example if a User rates a Movie then the relationship would probably have a score. This can be achieved with relationship entities. Read more about them at the OGM’s documentation.

Example project

There is an example project that you may use to test the bundle. Use the the steps below to install the project:

git clone git@github.com:neo4j-examples/movies-symfony-php-bolt.git
composer install

Import data fixture (https://neo4j.com/developer/example-project/#_data_setup)

php bin/console server:run

Go to http://127.0.0.1:8000/

There is more information about the bundle in the repository on Github. Please give us comments and feedback of what we did right and how we can improve. Feel free to raise issues or contribute to the project.

Resources:

Frequently Asked Questions (FAQs) about Neo4j Symfony Bundle

What is the Neo4j Symfony Bundle?

The Neo4j Symfony Bundle is a powerful tool that integrates the Neo4j graph database with the Symfony PHP framework. It allows developers to leverage the power of graph databases, which are excellent for handling complex, interconnected data, within the Symfony environment. This bundle provides a range of features, including object mapping, a query builder, and support for Neo4j’s Cypher query language.

How do I install the Neo4j Symfony Bundle?

To install the Neo4j Symfony Bundle, you need to use Composer, a dependency management tool for PHP. Run the command composer require graphaware/neo4j-php-client-symfony in your terminal. This will download and install the bundle and its dependencies in your project. After installation, you need to enable the bundle in your Symfony application.

What is a Property Graph in Neo4j?

A property graph in Neo4j is a type of data model that uses nodes, relationships, and properties to represent and store data. Nodes represent entities, relationships connect nodes and describe how they are related, and properties are key-value pairs that add attributes to nodes and relationships. This model is particularly useful for representing complex, interconnected data.

How do I use the Query Builder in Neo4j Symfony Bundle?

The Query Builder in the Neo4j Symfony Bundle allows you to construct Cypher queries in a programmatic and object-oriented way. You can start by getting an instance of the Query Builder from the client, then use its methods to build your query. Once your query is built, you can execute it using the run method.

How do I map my objects in Neo4j Symfony Bundle?

The Neo4j Symfony Bundle provides an Object Graph Mapper (OGM) that allows you to map your PHP objects to nodes and relationships in the graph. You can annotate your classes and properties to define how they should be mapped. The OGM will then handle the conversion between your objects and the graph.

How do I handle errors in Neo4j Symfony Bundle?

The Neo4j Symfony Bundle throws exceptions when it encounters errors. You can catch these exceptions in your code and handle them as appropriate. The bundle provides several specific exception classes for different types of errors, such as ConnectionException for connection errors and CypherException for errors in Cypher queries.

Can I use Neo4j Symfony Bundle with Symfony 6 and PHP 8.2?

Yes, the Neo4j Symfony Bundle is compatible with Symfony 6 and PHP 8.2. However, you should always check the bundle’s documentation and the requirements of your specific project to ensure compatibility.

How do I configure the Neo4j Symfony Bundle?

You can configure the Neo4j Symfony Bundle in your Symfony application’s configuration files. The bundle provides a range of configuration options, including setting the connection details for your Neo4j database, configuring the OGM, and more.

How do I execute Cypher queries with Neo4j Symfony Bundle?

You can execute Cypher queries with the Neo4j Symfony Bundle using the run method of the client or the Query Builder. You can pass your Cypher query as a string to this method, along with any parameters for the query.

What are the benefits of using Neo4j Symfony Bundle?

The Neo4j Symfony Bundle brings the power of graph databases to the Symfony PHP framework. It allows you to handle complex, interconnected data with ease, provides a range of powerful features such as object mapping and a query builder, and integrates seamlessly with your Symfony application.

Tobias NyholmTobias Nyholm
View Author

Swedish 20-something developer who thinks open source is the best thing since they invented Nutella toasts. Working at Happyr.com by day and by night: HTTPlug, PHP-Cache, PHP-Translation, Geocoder-PHP and API clients like Mailgun, Github, LinkedIn, Auth0 etc etc

BrunoSbundlegraph databaseneo4jOOPHPPHPsymfonysymfony bundlesymfony frameworksymfony3
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week