toannk
11/23/2014 - 5:47 PM

Simple example of returning a generic object in a repository using Laravel.

Simple example of returning a generic object in a repository using Laravel.

<?php namespace App\Repositories;

use Illuminate\Support\Contracts\ArrayableInterface;
use Illuminate\Support\Contracts\JsonableInterface;

class RepositoryObject implements ArrayableInterface, JsonableInterface
{

    public function fill(array $attributes)
    {
        foreach ($attributes as $key => $value) {
            $this->$key = $value;
        }
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return (array)$this;
    }

    /**
     * Convert the object to its JSON representation.
     *
     * @param  int $options
     *
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this->toArray());
    }

    /**
     * Convert the object to a JSON string.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->toJson();
    }

}
<?php namespace App\Repositories\Items;

use App;
use App\Models\Item;
use App\Repositories\AbstractRepository;

class ItemDBRepository extends AbstractRepository
{

    public function __construct(Item $model)
    {
        parent::__construct($model);
    }
    
    public function findOrFail($id)
    {
        return $this->enhance($this->model->findOrFail($id));
    }

}
<?php namespace App\Repositories;

use App;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Collection;
use Illuminate\Support\Contracts\ArrayableInterface;

abstract class AbstractRepository
{

    protected $model;

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



    /**
     * Parses a value in a nested way as deep as possible, in an
     * effort to normalize itself and its contents.
     *
     * Mainly converts (Eloquent, etc) objects to standardized RespitoryObjects.
     *
     * @param mixed $value
     *
     * @return RepositoryObject|Collection|mixed
     */
    protected function normalize($value)
    {
        $isCollection = false;

        // TODO: extensively test with various values!

        // If it's an array
        if (is_array($value) || $value instanceof Collection) {

            $isCollection = true;

            if ($value instanceof Collection) {
                $value = $value->toArray();
            }

            foreach ($value as &$item) {
                // We're not there yet!
                // Some nesting to get every object normalized
                $item = $this->normalize($item);
            }

            // Some conversion because we want collections and not arrays
            // (collections are way more powerful)
            if (is_array($value)) {
                $value = App::make('Illuminate\Support\Collection', [$value]);
            }
        }

        // An object that implements ArrayableInterface (e.g. Eloquent)
        // needs to be converted to array so its values represent
        // whatever is visible or hidden
        elseif (is_object($value) && $value instanceof ArrayableInterface) {
            $value = $value->toArray();
        }

        // A regular object that we just cast
        // to an array
        elseif (is_object($value)) {
            $value = (array)$value;
        }

        // Fill the custom repository object, but only
        // if it's an array (otherwise making it a
        // repository object would be too much normalization)
        // Also don't do this for collections
        if (is_array($value) && !$isCollection) {
            $object = App::make('App\Repositories\RepositoryObject');
            $object->fill($value);
        }

        // Return either the normalised object
        // or the original value (not an array or object, or a nested call)
        return isset($object) ? $object : $value;
    }

    /**
     * Perform some content validation on the provided value.
     *
     * @param mixed $value
     *
     * @throws ModelNotFoundException
     */
    protected function validate($value)
    {
        // Is it a Collection?
        if ($value instanceof Collection) {

            // Does it have any content?
            if (count($value) <= 0) {

                throw (new ModelNotFoundException())->setModel($this->model);
            }
        }
    }

    /**
     * Wrapper to normalize and validate a result.
     *
     * @param mixed $result
     *
     * @return \App\Repositories\RepositoryObject|\Illuminate\Support\Collection|mixed
     */
    protected function enhance($result)
    {
        $result = $this->normalize($result);

        $this->validate($result);

        return $result;
    }



    /**
     * Handle every public call that's not overridden and reroute it to the model.
     *
     * @param $name
     * @param $arguments
     *
     * @return \App\Repositories\RepositoryObject|\Illuminate\Support\Collection|mixed
     */
    public function __call($name, $arguments)
    {
        // TODO: might need some more review and work
        return $this->enhance(call_user_func_array([$this->model, $name], $arguments));
    }
}