Skip to content

Instantly share code, notes, and snippets.

@eoconnell
Created December 24, 2012 04:33
Show Gist options
  • Save eoconnell/4367545 to your computer and use it in GitHub Desktop.
Save eoconnell/4367545 to your computer and use it in GitHub Desktop.
Experimenting with something similar to Laravel's best practices http://laravel.com/docs/models. Thoughts?
<?php namespace DAO;
class StorageException extends \Exception {}
class DuplicateRecordException extends \Exception {}
interface DAO {
/**
* Checks if an Entity exists in the datastore.
*
* @param Integer
* @return Boolean
*/
public function exists($id);
/**
* Puts an Entity in the datastore.
*
* @param Entity
* @return void
*/
public function putOne(Entity $entity);
/**
* Puts all Entities in the datastore.
*
* @param Array
*/
public function putAll(array $entities);
/**
* Gets an Entity by Id.
*
* @param Integer
* @return Entity
*/
public function getOne($id);
/**
* Gets all Entities from the datastore.
*
* @return Array
*/
public function getAll();
/**
* Deletes an Entity from the datastore.
*
* @param Integer
*/
public function deleteOne($id);
/**
* Deletes all Entities from the datastore.
*
* @param Array
*/
public function deleteAll(array $entities);
}
<?php namespace DAO;
/**
* This class is not meant to be used on its own. In order to work nicely with
* Eloquent and with our Entities, the subclass must implement a few things.
*
* @example
*
* class EloquentEntityDAO extends EloquentDAO {
*
* public static $table = 'tablename';
*
* public static $timestamps = true;
*
*
* protected function toEntity($model) { ... }
*
* protected function fromEntity($entity) { ... }
*
* ...
* }
*/
abstract class EloquentDAO extends \Eloquent implements DAO {
/**
* Get a bare-bones Eloquent Model
*
* @return Laravel\Database\Eloquent\Model
*/
protected function model()
{
return new static;
}
/**
* Checks if an Entity exists in the datastore.
*
* @param Integer
* @return Boolean
*/
public function exists($id)
{
return (bool) $this->model()->find($id);
}
/**
* Puts an Entity in the datastore.
*
* @param Entity
* @return void
*/
public function putOne(Entity $entity)
{
throw new Exception("Not Implemented");
}
/**
* Puts all Entities in the datastore.
*
* @param Array
*/
public function putAll(array $entities)
{
throw new Exception("Not Implemented");
}
/**
* Gets an Entity by Id.
*
* @param Integer
* @return Entity
*/
public function getOne($id)
{
if ($this->exists($id))
{
return $this->toEntity($this->model()->find($id));
}
return null;
}
/**
* Gets all Entities from the datastore.
*
* @return Array
*/
public function getAll()
{
$entities = array();
$models = $this->model()->all();
foreach ($models as $model)
{
$entities[] = $this->toEntity($model);
}
return $entities;
}
/**
* Deletes an Entity from the datastore.
*
* @param Integer
*/
public function deleteOne($id)
{
$this->model()->find($id)->delete();
}
/**
* Deletes all Entities from the datastore.
*
* @param Array
*/
public function deleteAll(array $entities)
{
throw new Exception("Not Implemented");
}
}
<?php namespace DAO;
use Entities\Team;
class EloquentTeamDAO extends EloquentDAO implements TeamDAO {
public static $table = 'teams';
public static $timestamps = false;
protected function toEntity($model)
{
$entity = new Team();
$entity->id = (int) $model->id;
$entity->name = $model->name;
$entity->location = $model->location;
return $entity;
}
protected function fromEntity($entity)
{
throw new Exception("Not Implemented");
}
public function filter($query)
{
$teams = \DB::query("SELECT * FROM teams WHERE location || ' ' || name LIKE '%{$query}%'");
$entities = array();
foreach ($teams as $team)
{
$entities[] = $this->toEntity($team);
}
return $entities;
}
/**
* Adds a User to a Team and assigns their Role.
*
* @throws DuplicateRecordException
* @throws StorageException
*/
public function joinTeam($id, $user_id, $role)
{
$date = new \DateTime();
$timestamp = $date->format('Y-m-d H:i:s');
// check if duplicate
// should be a database constraint but for now...
$duplicate = (bool) \DB::first("
SELECT id
FROM user_team
WHERE user_id = {$user_id}
AND team_id = {$id}
");
if ($duplicate)
{
throw new DuplicateRecordException();
}
$result = \DB::query("
INSERT INTO user_team (user_id, team_id, role, created_at, updated_at)
VALUES ({$user_id}, {$id}, '{$role}', '{$timestamp}', '{$timestamp}')
");
if (! $result)
{
throw new StorageException();
}
}
}
<?php namespace Entities;
class Team extends Entity {
/**
* @var Integer
*/
public $id;
/**
* @var String
*/
public $name;
/**
* @var String
*/
public $location;
}
<?php namespace DAO;
interface TeamDAO extends DAO {
/**
* Filters Teams based on the query string.
*/
public function filter($query);
/**
* Adds a User to a Team and assigns their Role.
*/
public function joinTeam($id, $user_id, $role);
}
<?php
class Teams_Controller extends Controller {
private $teamDAO;
public function __construct()
{
$this->teamDAO = IoC::resolve('TeamDAO');
}
/**
* Returns all Teams.
*
* @access GET
* @param q - query string.
*
* @return JSON
*/
public function action_index()
{
$filter = Input::query('q');
if (! is_null($filter))
{
$teams = $this->teamDAO->filter($filter);
}
else
{
$teams = $this->teamDAO->getAll();
}
return Response::json($teams);
}
...
}
@eoconnell
Copy link
Author

Thanks William,

To me, the reason you would build an abstraction layer between your application code and your database is to be flexible.

For example, if I code Eloquent specific functionality directly in my controllers and at some point decide to use a different ORM or use Mongo instead of mySQL, then I'm going to end up rewriting most of my application. By coding controllers to an interface, the required implementation can be injected into the controller using dependency injection.

Dependency injection is also nice because it allows you to easily inject mock objects during tests. So you get this added bonus of more testable code.

The code in this gist is a bit rough but I'm in the process of developing an implementation for Laravel 4. If you are interested I can write a blog post about it when I finish.

I hope this helps.

@williamcolbert
Copy link

Hey Evan,

Thanks for the response makes sense now, gonna try a few things to see if I can fit it together in my mind.

That would be great if you can write a blog post when you get done.

Could you link me to your blog ?

Thanks .

@BenaddiRar
Copy link

Great Job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment