Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Everyman Software Musings on software development, open source, PHP and current projects. 2011-11-05 Development
- Setup for Neo4j and PHP: Part 2 This is Part 2 of a series on setting up a development environment for building
- projects using the graph database Neo4j and PHP. In Part 1 of this series, we set up unit test and development
- databases. In this part, we'll build a skeleton project that includes unit tests, and a minimalistic user interface.
- All the files will live under a directory on our web server. In a real project, you'll probably want only the user
- interface files under the web server directory and your testing and library files somewhere more protected. Also, I
- won't be using any specific PHP framework. The principles in the code below should apply equally to any framework you
- decide to use. Create the Project and Testing HarnessCreate a project in a directory on your web server. For this
- project, mine is "/var/www/neo4play" on my local host. We'll also need our Neo4j client library. > cd
- /var/www/neo4play > mkdir -p tests/unit > mkdir lib > cp ~/Downloads/neo4jphp.phar lib/ > echo "<?php phpinfo(); ?>"
- > index.php Test the setup by browsing to http://localhost/neo4play. You should see the output of `phpinfo`. Now
- we'll create a bootstrap file that we can include to do project-wide and environment specific setup. Call this file
- "bootstrap.php" in the root project directory. <?php require_once(__DIR__.'/lib/neo4jphp.phar'); error_reporting(-1);
- ini_set('display_errors', 1); if (!defined('APPLICATION_ENV')) { define('APPLICATION_ENV', 'development'); } $host =
- 'localhost'; $port = (APPLICATION_ENV == 'development') ? 7474 : 7475; $transport = new
- Everyman\Neo4j\Transport($host, $port); $client = new Everyman\Neo4j\Client($transport); The main point of this file
- at the moment is to differentiate between our development and testing environments, and set up our connection to the
- correct database. We do this by attaching the database client to the correct port based on an application constant.
- We'll use the bootstrap file to setup a different, unit testing specific bootstrap file. Create the following file as
- "tests/bootstap-test.php": <?php define('APPLICATION_ENV', 'testing'); require_once(__DIR__.'/../bootstrap.php');
- $transport->delete('/cleandb/secret-key'); The purpose of this file to to tell our application bootstrap that we are
- in the "testing" environment. Then it cleans out the database so that our tests run from a known state. Make sure
- "secret-key" in the `delete()` call is whatever you set in the Neo4j config file from Part 1. Tell PHPUnit to use our
- test bootstrap with the following config file, called "tests/phpunit.xml": <phpunit colors="true"
- bootstrap="./bootstrap-test.php"> <testsuite name="Neo4j Play Test Results"> <directory>./unit</directory>
- </testsuite> </phpunit> And because we're following TDD, we'll create our first test file,
- "tests/unit/ActorTest.php": <?php class ActorTest extends PHPUnit_Framework_TestCase { public function
- testCreateActorAndRetrieveByName() { $actor = new Actor(); $actor->name = 'Test Guy '.rand(); Actor::save($actor);
- $actorId = $actor->id; self::assertNotNull($actorId); $retrievedActor = Actor::getActorByName($actor->name);
- self::assertInstanceOf('Actor', $retrievedActor); self::assertEquals($actor->id, $retrievedActor->id);
- self::assertEquals($actor->name, $retrievedActor->name); } public function testActorDoesNotExist() { $retrievedActor
- = Actor::getActorByName('Test Guy '.rand()); self::assertNull($retrievedActor); } } So we know we want a domain
- object called "Actor" (apparently we're building some sort of movie application) and that Actors have names and ids.
- We also know we want to be able to look up an Actor by their name. If we can't find the Actor by name, we should get
- a `null` value back. Run the tests: > cd tests > phpunit Excellent, our tests failed! If you've been playing along,
- they probably failed because the "Actor" class isn't defined. Our next step is to start creating our domain objects.
- Defining the Application DomainSo far, we only have one domain object, and a test that asserts its behavior. In order
- to make the test pass, we'll need to connect to the database, persist entities to it, and then query it for those
- entities. For persisting our entities, we'll need a way to get the client connection in our "Actor" class and any
- other domain object classes we define. To do this, we'll create an application registry/dependency-injection
- container/pattern-buzzword-of-the-month class. Put the following in the file "lib/Neo4Play.php": <?php class Neo4Play
- { protected static $client = null; public static function client() { return self::$client; } public static function
- setClient(Everyman\Neo4j\Client $client) { self::$client = $client; } } Now our domain objects will have access to
- the client connection through `Neo4Play::client()` when we persist them to the database. It's time to define our
- actor class, in the file "lib/Actor.php": <?php use Everyman\Neo4j\Node, Everyman\Neo4j\Index; class Actor { public
- $id = null; public $name = ''; public static function save(Actor $actor) { } public static function
- getActorByName($name) { } } Requiring our classes and setting up the client connection is part of the bootstrapping
- process of the application, so we'll need to add some thing to "bootstrap.php": <?php
- require_once(__DIR__.'/lib/neo4jphp.phar'); require_once(__DIR__.'/lib/Neo4Play.php');
- require_once(__DIR__.'/lib/Actor.php'); // ** set up error reporting, environment and connection... **//
- Neo4Play::setClient($client); We have a stub class for the domain object. The tests will still fail when we run them
- again, but at least all the classes should be found correctly. Let's start with finding an Actor by name. With our
- knowledge of graph databases, we know this will involve an index lookup, and that we will get a Node object in
- return. If the lookup returns no result, we'll get a `null`. If we do get a Node back, we'll want to hold on to it,
- for updating the Actor later. Modify the Actor class with the following contents: class Actor { // protected $node =
- null; // public static function getActorByName($name) { $actorIndex = new Index(Neo4Play::client(), Index::TypeNode,
- 'actors'); $node = $actorIndex->findOne('name', $name); if (!$node) { return null; } $actor = new Actor(); $actor->id
- = $node->getId(); $actor->name = $node->getProperty('name'); $actor->node = $node; return $actor; } } The main thing
- we're trying to accomplish here is keeping our domain classes as Plain-Old PHP Objects, that don't require any
- special class inheritance or interface, and that hide the underlying persistence layer from the outside world. The
- tests still fail. We'll finish up our Actor class by saving the Actor to the database. class Actor { // public static
- function save(Actor $actor) { if (!$actor->node) { $actor->node = new Node(Neo4Play::client()); }
- $actor->node->setProperty('name', $actor->name); $actor->node->save(); $actor->id = $actor->node->getId();
- $actorIndex = new Index(Neo4Play::client(), Index::TypeNode, 'actors'); $actorIndex->add($actor->node, 'name',
- $actor->name); } // } Run the tests again. If you see all green, then everything is working properly. To double
- check, browse to the testing instance webadmin panel http://localhost:7475/webadmin/#. You should see 2 nodes and 1
- property (Why 2 nodes? Because there is a node 0 -- the reference node -- that is not deleted when the database is
- cleaned out.) Build Something UsefulIt's time to start tacking on some user functionality to our application. Thanks
- to our work on the unit tests, we can create actors in the database and find them again via an exact name match.
- Let's expose that functionality. Change the contents of "index.php" to the following: <?php
- require_once('bootstrap.php'); if (!empty($_POST['actorName'])) { $actor = new Actor(); $actor->name =
- $_POST['actorName']; Actor::save($actor); } else if (!empty($_GET['actorName'])) { $actor =
- Actor::getActorByName($_GET['actorName']); } ?> <form action="" method="POST"> Add Actor Name: <input type="text"
- name="actorName" /> <input type="submit" value="Add" /> </form> <form action="" method="GET"> Find Actor Name: <input
- type="text" name="actorName" /> <input type="submit" value="Search" /> </form> <?php if (!empty($actor)) : ?> Name:
- <?php echo $actor->name; ?><br /> Id: <?php echo $actor->id; ?><br /> <?php elseif (!empty($_GET['actorName'])) : ?>
- No actor found by the name of "<?php echo $_GET['actorName']; ?>"<br /> <?php endif; ?> Browse to your index file.
- Mine is at http://localhost/neo4play/index.php. You should see the page you just created. Enter a name in the "Add
- Actor Name" box and click the "Add" button. If everything went according to plan, you should see the actor name and
- the id assigned to the actor by the database. Try finding that actor using the search box. Note the actor's id.
- Browse to http://localhost:7474/webadmin/# and click the "Data browser" tab. Enter the actor id in the text box at
- the top. The node you created when you added the actor should show up. The interesting thing is that our actual
- application doesn't know anything about how the Actors are stored. Nothing in "index.php" references graphs or nodes
- or indexes. This means that, in theory, we could swap out the persistence layer for a SQL databases later, or
- MongoDB, or anything else, and nothing in our application would have to change. If we started with a SQL database, we
- could easily transition to a graph database. Explore the Wonderful World of GraphsYour development environment is now
- set up, and your application is bootstrapped. There's a lot more to add to this application, including creating
- movies, and linking actors and movies together. Maybe you'll want to add a social aspect, with movie recommendations.
- Graph databases are powerful tools that enable such functionality to be added easily. Go ahead and explore the rest
- of the Neo4jPHP library (wiki and API). Also, be sure to checkout the Neo4j documentation, especially the sections
- about the REST API, Cypher and Gremlin (two powerful graph querying and processing languages.) All the code for this
- sample application is available as a gist: http://gist.github.com/1341833. Happy graphing! by Josh Adell on
- 11/05/2011 Email ThisBlogThis!Share to TwitterShare to Facebook Labels: everyman, graph, neo4j, php 5 comments:
- Daniel3dFebruary 24, 2012 11:24 AMHi i get this error when i try to add some node whit the index.php" Fatal error:
- Uncaught exception 'Everyman\Neo4j\Exception' with message 'Unable to search index [500]: Headers: Array ( ) Body:
- Array ( [error] => Couldn't resolve host 'xubuntu' [6] ) ' in
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php:116 Stack trace: #0
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command/SearchIndex.php(95):
- Everyman\Neo4j\Command->throwException('Unable to searc...', 500, Array, Array) #1
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php(69):
- Everyman\Neo4j\Command\SearchIndex->handleResult(500, Array, Array) #2
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Client.php(571): Everyman\Neo4j\Command->execute() #3
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Client.php(513):
- Everyman\Neo4j\Client->runCommand(Object(Everyman\Neo4j\Command\SearchIndex)) #4
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Index.php(77):
- Everyman\Neo4j\Client->searchIndex(Object(Everyman\Neo4j\Index), 'name',
- inphar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php on line 116
- "ReplyDeleteDaniel3dFebruary 24, 2012 11:29 AMSorry this one is when i try to save. another one is when i search
- Please help .AND THANKS JOSH ADELL GREAT JOB." Fatal error: Uncaught exception 'Everyman\Neo4j\Exception' with
- message 'Unable to create node [500]: Headers: Array ( ) Body: Array ( [error] => Couldn't resolve host 'xubuntu' [6]
- ) ' in phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php:116 Stack trace: #0
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command/CreateNode.php(68):
- Everyman\Neo4j\Command->throwException('Unable to creat...', 500, Array, Array) #1
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php(69):
- Everyman\Neo4j\Command\CreateNode->handleResult(500, Array, Array) #2
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Client.php(571): Everyman\Neo4j\Command->execute() #3
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Client.php(478):
- Everyman\Neo4j\Client->runCommand(Object(Everyman\Neo4j\Command\CreateNode)) #4
- phar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Node.php(94):
- Everyman\Neo4j\Client->saveNode(Object(Everyman\Neo4j\Node)) #5 /var/www/neo4
- inphar:///var/www/neo4play/lib/neo4jphp.phar/lib/Everyman/Neo4j/Command.php on line 116"ReplyDeleteJosh AdellFebruary
- 24, 2012 12:10 PMIf you are using the example scripts from the Gist, you will need to change the $host variable in
- bootstrap.php to be 'localhost' or whatever the hostname of your Neo4j instance is.ReplyDeleteDaniel3dFebruary 29,
- 2012 4:49 PMThanks work...ReplyDeleteKarthikbaluJuly 21, 2012 3:21 PMWow ur awesomeReplyDeleteAdd commentLoad more...
- Newer Post Older Post Home Subscribe to: Post Comments (Atom) Josh Adell Durham, NC Software developer, tinkerer,
- armchair philosopher View my complete profile Home Projects About Me Labels php everyman neo4j javascript nodejs
- graph box2d unit test agile rest canvas git jaded websockets zend best practice citizen cloud codeigniter composer
- design pattern dropbox gearman gremlin json packaging phar phing phpfog prenup scrum silex targetprocess tropo vows
- zombie Blog Archive ? 2012 (8) ? July (1) No Best Practices Yet ? May (1) Neo4jPHP Available as a Composer Package
- ? April (2) Runtime Expectations Javascript Throwdown Data Mapper Injection in PHP Objects ? March (1) GetSet
- Methods vs. Public Properties ? February (1) Similarity-based Recommendation Engines ? January (2) Command Invoker
- Pattern with the Open/Closed Princ... Funkatron's MicroPHP Manifesto ? 2011 (24) ? December (3) PHP Fog Quickstart
- Decode JSON in Bash with PHP Codeworks '11 Raleigh Rundown ? November (2) Development Setup for Neo4j and PHP: Part
- 2 Development Setup for Neo4j and PHP: Part 1 ? October (1) A Humbling Reference ? September (1) Phar Flung Phing
- ? August (1) Getting Neo4jPHP working with CodeIgniter 2 ? July (4) Performance of Graph vs. Relational Databases
- TriPUG Meetup slides agile Adoption on a Non-Technical Team Neo4jPHP beta released ? June (2) Path finding with
- Neo4j Neo4j for PHP ? May (2) Logging User Sessions Across Requests Interview with Bulat Shakirzyanov ? April (5)
- Tropo-Turing Collision: Voice Chat Bots with Tropo... Syntactic sugar for Vows.js Dynamic Assets: Part III - Entry
- Forms Dynamic Assets: Part II - Construction Dynamic Assets: Part I - Definition ? March (2) Undead Wedding Bash
- sockets ? February (1) Jaded available on GitHub ? 2010 (19) ? December (3) The Goal Git, TargetProcess and
- Hashtags Private Git repository with Dropbox ? November (3) JadedPHP controllers New domains Models can be so Jaded
- ? October (2) Box2dnode Available via npm Global Pub-Sub ? August (2) Mocking built-in functions in PHP Josh's
- Modest Proposals ? July (2) A Helpful Analogy, or, Why You Should Be Testing Y... Parallel Testing in the Cloud with
- Feature Branche... ? June (6) Emitting Physics Events with Box2d and Node Canvas Transforms and Origin Re-Mapping
- Real-time Multi-player In-Browser Gaming: Air Hock... Blog Title Change box2dnode available Nodejs and Physics ? May
- (1) Copyright © 2010 Josh Adell. Simple template. Powered by Blogger.
Advertisement
Comments
-
- Thank you for some other informative website. The place else may just I get that kind of information written in such a perfect method? I have a venture that I am simply now running on, and I’ve been at the glance out for such info. <a href="https://assumeworld.com/gta-6-trailer-what-we-know-so-far/">GTA 6</a>
Add Comment
Please, Sign In to add comment