dweineratl/laravel-model-abstraction-helper

Not every Laravel model supports Doctrine. This is a helper to abstract getting column information from models

Downloads

4

Stars

0

Version

1.0.1

Laravel Model Abstraction Helper

Why go to all this effort?

I'm trying to backfill tests in my Laravel project, and I really wanted to use Laravel Test Factory Generator to generate factories for the tests. But I had a problem -- I used Jens Segers' Laravel MongoDB Eloquent extension for Laravel.

Due to the nature of how MongoDB works, it doesn't have a Doctrine Driver. So I couldn't use it. Or could I? I have been adding full PHP DocBlocks to my models, using @property to help with hinting and variable completion in my IDE.

I was discussing this approach with Jason McCreary because he was the one that pointed me towards Laravel Test Factory Generator. He suggested abstracting it out into a factory that could use Doctrine, DocBlocks, custom drivers and ultimately, fallback to using the $fillable array that should be present in a Laravel Model, and Laravel Model Abstraction Helper was born.

Install

Require this package with composer using the following command:

composer require dweineratl/laravel-model-abstraction-helper

How to use

For example, Laravel Test Factory Generator uses this code snippet to get the column names and types for a Model:

    protected function getPropertiesFromTable($model)
    {
        $table = $model->getConnection()->getTablePrefix() . $model->getTable();
        $schema = $model->getConnection()->getDoctrineSchemaManager($table);
        $databasePlatform = $schema->getDatabasePlatform();
        $databasePlatform->registerDoctrineTypeMapping('enum', 'string');

        $platformName = $databasePlatform->getName();
        $customTypes = $this->laravel['config']->get("ide-helper.custom_db_types.{$platformName}", array());
        foreach ($customTypes as $yourTypeName => $doctrineTypeName) {
            $databasePlatform->registerDoctrineTypeMapping($yourTypeName, $doctrineTypeName);
        }

        $database = null;
        if (strpos($table, '.')) {
            list($database, $table) = explode('.', $table);
        }

        $columns = $schema->listTableColumns($table, $database);

        if ($columns) {
            foreach ($columns as $column) {
                $name = $column->getName();
                if (in_array($name, $model->getDates())) {
                    $type = 'datetime';
                } else {
                    $type = $column->getType()->getName();
                }
                if (!($model->incrementing && $model->getKeyName() === $name) &&
                    $name !== $model::CREATED_AT &&
                    $name !== $model::UPDATED_AT
                ) {
                    if (!method_exists($model, 'getDeletedAtColumn') || (method_exists($model, 'getDeletedAtColumn') && $name !== $model->getDeletedAtColumn())) {
                        $this->setProperty($name, $type);
                    }
                }
            }
        }
    }

It can be refactored to

    protected function getPropertiesFromTable($model)
    {
        $columns = ModelAbstractionFactory::getColumns($model);

        if ($columns) {
            foreach ($columns as $column) {
                $name = $column->getName();
                if (in_array($name, $model->getDates())) {
                    $type = 'datetime';
                } else {
                    $type = $column->getType();
                }
                if (!($model->incrementing && $model->getKeyName() === $name) &&
                    $name !== $model::CREATED_AT &&
                    $name !== $model::UPDATED_AT
                ) {
                    if (!method_exists($model, 'getDeletedAtColumn') || (method_exists($model, 'getDeletedAtColumn') && $name !== $model->getDeletedAtColumn())) {
                        $this->setProperty($name, $type);
                    }
                }
            }
        }
    }

Drivers

Laravel Model Abstraction Helper uses the concept of "drivers" to handle different types of models. Laravel Model Abstraction Helper ships with three drivers. The ModelAbstractionFactory will detect which driver to use

  • Doctrine Driver

    This driver is for models using a RDBMS that uses Doctrine, such as MySQL, Postgres or SQLite.

  • DocBloc

    This driver is for models that don't use a traditional RDBMS such as MongoDB that does not use Doctrine and has a PHP DocBloc with @property tags

  • Fillable

    This driver is the fallback when the model does not use Doctrine and does not have a DocBlock with at least one @property tag. It uses the $fillable array that should be present in every Laravel model

Class Methods

ModelAbstractionFactory

The ModelAbstractionFactory has two public static methods:

  /**
   * Factory to return an abstraction class to get information about a model
   *
   * @param $model
   *
   * @return \Dweineratl\LaravelModelHelper\Driver\DocBlocDriver|\Dweineratl\LaravelModelHelper\Driver\DoctrineDriver|\Dweineratl\LaravelModelHelper\Driver\FillableDriver
   * @throws \ReflectionException
   */
  public static function create($model)
  
  /**
   * Instantiate a LaravelModelHelper driver and return an array of Dweineratl\LaravelModelHelper\Column objects
   * 
   * @param $model
   * @return array
   * @throws \ReflectionException
   */
    public static function getColumns($model)

DriverInterface

Every driver implements the Dweineratl\LaravelModelHelper\DriverInterface which only has a single method

  /**
   * Process the model and return an array of Dweineratl\LaravelModelHelper\Column objects
   * 
   * @param \Illuminate\Database\Eloquent\model $model
   *
   * @return array
   */
  public function getColumns(Eloquent $model);

Column

  /**
   * Column constructor.
   *
   * @param $name Column Name
   * @param $type Column Type
   */
  public function __construct($name, $type)

  /**
   * Get the column name
   *
   * @return string
   */
  public function getName()

  /**
   * Get the column type
   *
   * @return string
   */
  public function getType()

  /**
   * Set the column name
   *
   * @param $name
   */
  public function setName($name)

  /**
   * Set the column type
   *
   * @param $type
   */
  public function setType($type)

Custom Drivers

To support other methods of obtaining the columns for a model, Laravel Model Abstraction Helper supports drivers that are in the Dweineratl\LaravelModelHelper\Driver namespace, and the class name is value of config('database.default') concated with 'Driver' and in PascalCase (i.e, Dweineratl\LaravelModelHelper\Driver\CouchdbDriver). It must implement the Dweineratl\LaravelModelHelper\DriverInterface at a minimum, providing the getColumns() method that returns an array of Dweineratl\LaravelModelHelper\Column objects. ModelAbstractionFactory will first check if the model supports Doctrine. If it does not support Doctrine, it will then get the value of the default database driver using config('database.default') and check if a custom driver for that database exists. If it does not, it will then check to see if the model has a suitable DocBlock. Finally, if none of the previous drivers are usable, the default fallback of using the $fillable array will be used.

License

The Laravel Model Abstraction Helper is free software licensed under the MIT license.

dweinerATL

Author

dweinerATL