Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit c576b5f60042ae86a8737d40859b6e36894316ef @jwage jwage committed Apr 5, 2010
@@ -0,0 +1,110 @@
+# Doctrine 2 REST Server and Client
+
+The Doctrine 2 REST server and client component is both an easy way to spin up
+REST services for your Doctrine 2 entities as well as a way to work with REST
+services via an ActiveRecord style implementation similiar to ActiveResource in
+Ruby on Rails!
+
+## Introduction
+
+The basic concept is simple, you have a REST service (http://api.people.com/person)
+and you want to interact with it through a simple ActiveRecord style interface.
+
+First we can retrieve a person:
+
+ $person = Person::find(1); // GET http://api.people.com/person/1.xml
+
+Now we can change some properties of that person:
+
+ $person->setName('Jonathan H. Wage');
+
+Once we're done we can simply save it and the appropriate REST call will be made:
+
+ $person->save(); // POST http://api.people.com/person/1.xml (name=Jonathan H. Wage)
+
+## Client
+
+The REST client is an ActiveRecord style implementation for working with REST
+services. All you need to do is define some PHP classes that are mapped to some
+REST service on the web. Here is an example where we map a Person to
+http://api.people.com/person:
+
+ <?php
+
+ namespace Entities;
+
+ use Doctrine\REST\Client\Entity;
+
+ class Person extends Entity
+ {
+ private $id;
+ private $name;
+
+ public static function configure(EntityConfiguration $entityConfiguration)
+ {
+ $entityConfiguration->setUrl('http://api.people.com');
+ $entityConfiguration->setName('person');
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+ }
+
+Now when we perform some actions it will generate the appropriate REST request,
+execute it, transform the response and hydrate the results to your PHP objects.
+
+ $person = new Person();
+ $person->setName('Jonathan H. Wage');
+ $person->save(); // PUT http://api.people.com/person.xml (name=Jonathan H. Wage)
+
+We can retrieve that person again now:
+
+ $person = Person::find($person->getId()); // GET http://api.people.com/person/1.xml
+
+Or you can retrieve all Person objects:
+
+ $persons = Person::findAll();
+
+## Server
+
+The Doctrine 2 REST server allows you to easily expose your entities through some
+REST services. This is the raw low level server and does not include any routing
+or URL parsing so you would need to implement in some existing framework that
+has routing like Symfony or Zend Framework.
+
+All you need to do is create a new REST server instance and pass it the instance
+of your EntityManager you want to expose the entities for and an array representing
+the server request you want to process:
+
+ $request = array(
+ '_method' => 'get',
+ '_format' => 'xml',
+ '_entity' => 'user',
+ '_action' => 'get',
+ '_id' => 1
+ );
+
+ $server = new \Doctrine\REST\Server\Server($em, $request);
+ $server->addEntityAlias('Entities\User', 'user');
+
+ $xml = $server->execute();
+
+The above would retrieve the User with the id of 1 and return an XML document
+like the following:
+
+ <user>
+ <id>1</id>
+ <username>jwage</username>
+ </user>
@@ -0,0 +1,110 @@
+<?php
+/*
+ * $Id$
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+*/
+
+namespace Doctrine\REST\Client;
+
+/**
+ * Basic class for issuing HTTP requests via PHP curl.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 2.0
+ * @version $Revision$
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ */
+class Client
+{
+ const POST = 'POST';
+ const GET = 'GET';
+ const PUT = 'PUT';
+ const DELETE = 'DELETE';
+
+ public function post(Request $request)
+ {
+ $request->setMethod(Client::POST);
+ return $this->execute($request);
+ }
+
+ public function get(Request $request)
+ {
+ $request->setMethod(Client::GET);
+ return $this->execute($request);
+ }
+
+ public function put(Request $request)
+ {
+ $request->setMethod(Client::PUT);
+ return $this->execute($request);
+ }
+
+ public function delete(Request $request)
+ {
+ $request->setMethod(Client::DELETE);
+ return $this->execute($request);
+ }
+
+ public function execute(Request $request)
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $request->getUrl());
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
+
+ $username = $request->getUsername();
+ $password = $request->getPassword();
+
+ if ($username && $password) {
+ curl_setopt ($ch, CURLOPT_USERPWD, $username . ':' . $password);
+ }
+
+ switch ($request->getMethod()) {
+ case self::POST:
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request->getParameters()));
+ break;
+ case self::DELETE:
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
+ break;
+ case self::PUT:
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request->getParameters()));
+ break;
+ case self::GET:
+ default:
+ break;
+ }
+
+ $result = curl_exec($ch);
+
+ if ( ! $result) {
+ $errorNumber = curl_errno($ch);
+ $error = curl_error($ch);
+ curl_close($ch);
+
+ throw new \Exception($errorNumer . ': ' . $error);
+ }
+
+ curl_close($ch);
+
+ return $request->getResponseTransformerImpl()->transform($result);
+ }
+}
@@ -0,0 +1,133 @@
+<?php
+/*
+ * $Id$
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+*/
+
+namespace Doctrine\REST\Client;
+
+/**
+ * Abstract entity class for REST entities to extend from to give ActiveRecord
+ * style interface for working with REST services.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 2.0
+ * @version $Revision$
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ */
+abstract class Entity
+{
+ protected static $_manager;
+
+ public static function setManager(Manager $manager)
+ {
+ self::$_manager = $manager;
+ }
+
+ public function toArray()
+ {
+ return get_object_vars($this);
+ }
+
+ public function exists()
+ {
+ return self::$_manager->entityExists($this);
+ }
+
+ public function getIdentifier()
+ {
+ return self::$_manager->getEntityIdentifier($this);
+ }
+
+ public static function generateUrl(array $options = array())
+ {
+ $configuration = self::$_manager->getEntityConfiguration(get_called_class());
+ return $configuration->generateUrl($options);
+ }
+
+ public static function find($id, $action = null)
+ {
+ return self::$_manager->execute(
+ get_called_class(),
+ self::generateUrl(get_defined_vars()),
+ Client::GET
+ );
+ }
+
+ public static function findAll($action = null, $parameters = null)
+ {
+ return self::$_manager->execute(
+ get_called_class(),
+ self::generateUrl(get_defined_vars()),
+ Client::GET, $parameters
+ );
+ }
+
+ public function save($action = null)
+ {
+ $parameters = $this->toArray();
+ $exists = $this->exists();
+ $method = $exists ? Client::POST : Client::PUT;
+ $id = $exists ? $this->getIdentifier() : null;
+ $path = $this->generateUrl(get_defined_vars());
+ return self::$_manager->execute($this, $path, $method, $parameters, $action);
+ }
+
+ public function delete($action = null)
+ {
+ $id = $this->getIdentifier();
+ return self::$_manager->execute(
+ $this, $this->generateUrl(get_defined_vars()), Client::DELETE
+ );
+ }
+
+ public function post($action = null)
+ {
+ $id = $this->getIdentifier();
+ return self::$_manager->execute(
+ $this, $this->generateUrl(get_defined_vars()),
+ Client::POST, $this->toArray()
+ );
+ }
+
+ public function get($action = null)
+ {
+ return self::$_manager->execute(
+ $this, $this->generateUrl(get_defined_vars()),
+ Client::GET, $this->toArray()
+ );
+ }
+
+ public function put($action = null)
+ {
+ return self::$_manager->execute(
+ $this, $this->generateUrl(get_defined_vars()),
+ Client::PUT, $this->toArray()
+ );
+ }
+
+ public static function execute($method, $action, $parameters = null)
+ {
+ return self::$_manager->execute(
+ get_called_class(),
+ self::generateUrl(get_defined_vars()),
+ $method, $parameters
+ );
+ }
+}
Oops, something went wrong.

1 comment on commit c576b5f

Please sign in to comment.