Johannes Siipola

How to use Eloquent ORM migrations outside Laravel

2016-01-18

Laravel's Eloquent is one of the most fluent and expressive ORM's available for PHP. However, unlike some of its competitors like Doctrine or Propel, it's highly tied to the Laravel ecosystem in some ways. It's still possible to use Eloquent outside Laravel with a bit of work. In this example, we are using Slim Framework, but most of the code will work in any framework of your choice, even in plain PHP if you are into that.

If you use Eloquent outside Laravel, you will be missing one of it's best features: database migrations. Using migrations you can store your database structure in code. This will make make updating production servers and working with co-workers a breeze. There's no need to open up MySQL command line or client, just run a few commands and your database it up to date.

There has been some effort in order to use Laravel migrations outside the framework, but it hasn't been possible because the migrations require a complete instance of Laravel to run. In this tutorial, we are using framework agnostic Phinx migration library instead.

Installing packages

We are using Composer to manage our packages so you need to have Composer installed. If you haven't, you can refer to its installation guide. Using composer, install Slim Framework by executing this command on your command line.

composer require 'slim/slim:3.*'

After Slim is installed, you need to install Eloquent ORM which lives in the illuminate/database package.

composer require 'illuminate/database:5.2.*'

After both of these are installed, you need to install Phinx, which is a library we are using to create and run the migrations.

composer require 'robmorgan/phinx:0.5.*'

Setting up the project

Database configuration

First, we need to create a database configuration file. This file will be used both by the application and the migrations. Create a new file called config.php in the root of your project and store your database configuration there.

<?php
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'myproject');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');
define('DB_PORT', 3306);

Autoloader

It's a good practice to use and autoloader in your project. In this example, we use composer's own autoloader to load out classes. Open your composer.json file and add the following after the require section

"autoload": {
  "psr-4": {
    "MyProject\\": "src"
  }
}

This means we are going to load all files beneath the src folder under the namespace MyProject.

In the end, your composer.json should look something like this:

{
  "require": {
    "slim/slim": "^2.6",
    "illuminate/database": "^5.1",
    "robmorgan/phinx": "^0.4.4",
  },
  "autoload": {
    "psr-4": {
      "MyProject\\": "src"
    }
  }
}

After this, you need to regenerate the autoloader so it knows where your classes are stored. Run this command.

composer dump-autoload

Migrations

Migrations enable you to store your database structure in the version control. No more dumping MySQL databases in the version control!

Migration Class

First of all, we need to extend the Phinx migration class to include the Laravel migration methods.

Create a new file called src/Migration/Migration.php with the following content:

<?php

namespace MyProject\Migration;

use Illuminate\Database\Capsule\Manager as Capsule;
use Phinx\Migration\AbstractMigration;

class Migration extends AbstractMigration {
    /** @var \Illuminate\Database\Capsule\Manager $capsule */
    public $capsule;
    /** @var \Illuminate\Database\Schema\Builder $capsule */
    public $schema;

    public function init()
    {
        $this->capsule = new Capsule;
        $this->capsule->addConnection([
          'driver'    => 'mysql',
          'host'      => DB_HOST,
          'port'      => DB_PORT,
          'database'  => DB_NAME,
          'username'  => DB_USER,
          'password'  => DB_PASSWORD,
          'charset'   => 'utf8',
          'collation' => 'utf8_unicode_ci',
        ]);

        $this->capsule->bootEloquent();
        $this->capsule->setAsGlobal();
        $this->schema = $this->capsule->schema();
    }
}

Here we are creating a new Illuminate Database instance in the init() method of the Phinx migration and starting up the Eloquent ORM.

Configuration

Next, we need to create a configuration file for Phinx so it knows which migration class and database credentials to use. Create a new file called config-phinx.php in the root of your project with the following contents:

<?php
require 'config.php';
return [
  'paths' => [
    'migrations' => 'migrations'
  ],
  'migration_base_class' => '\MyProject\Migration\Migration',
  'environments' => [
    'default_migration_table' => 'phinxlog',
    'default_database' => 'dev',
    'dev' => [
      'adapter' => 'mysql',
      'host' => DB_HOST,
      'name' => DB_NAME,
      'user' => DB_USER,
      'pass' => DB_PASSWORD,
      'port' => DB_PORT
    ]
  ]
];

Creating a migration

Now you are ready to create your first migration. Run the following command in your console:

php vendor/bin/phinx create MyFirstMigration -c config-phinx.php

Phinx will ask you about creating the migrations directory, answer yes.

You can find your first migration in the migrations directory, it's marked by a timestamp and migration name. Open it up.

By default, Phinx uses a change method. Because Eloquent doesn't support that, we need to use more traditional up() and down() methods. The up method describes the database changes we want to create and the down method reverses them. Here's an example of a migration. You can check all available properties in the Laravel documentation.

<?php

use \MyProject\Migration\Migration;

class MyFirstMigration extends Migration
{
    public function up()
    {
        $this->schema->create('widgets', function(Illuminate\Database\Schema\Blueprint $table){
            // Auto-increment id

            $table->increments('id');
            $table->integer('serial_number');
            $table->string('name');
            // Required for Eloquent's created_at and updated_at columns

            $table->timestamps();
        });
    }
    public function down()
    {
        $this->schema->drop('widgets');
    }
}

Running your first migration

When your migration is ready, you can run it using the command

php vendor/bin/phinx migrate -c config-phinx.php

Note: if you are using Vagrant, make sure to run this command inside the Vagrant box!

Now, if you check your database using an application like Sequel Pro or HeidiSQL or the mysql command line client, you should see the new tables in your database.

Next, let's add some example data and view it.

Adding a model class

Because an ORM works by translating your database rows into objects, we need to create a model to hold our data. Let's start by creating a new model in the file src/Model/Widget.php.

<?php
namespace MyProject\Model;

use Illuminate\Database\Eloquent\Model;

class Widget extends Model
{
}

Because we don't need to specify any fancy relations, that is enough for now.

Creating the application

To add and view data in our database, we need to create a simple application. For this, we are using Slim Framework, but the same concepts work in any Framework, even in plain PHP.

Create a new index.php file in your project root

<?php
require "vendor/autoload.php";
require "config.php";
use Illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
  'driver' => 'mysql',
  'host' => DB_HOST,
  'port' => DB_PORT,
  'database' => DB_NAME,
  'username' => DB_USER,
  'password' => DB_PASSWORD,
  'charset' => 'utf8',
  'collation' => 'utf8_unicode_ci',
]);
$capsule->bootEloquent();
$capsule->setAsGlobal();

This loads our database connection and boots eloquent. Next, let's create a route to add some content.

$app = new \Slim\App();

$app->get('/create', function(){
    $widget = new \MyProject\Model\Widget();
    $widget->serial_number = 123;
    $widget->name = 'My Test Widget';
    $widget->save();
    echo 'Created!';
});

Lastly, we need to start our application:

$app->run();

Now when you visit the URL /create in your browser, you should see the message "Created!" and the new model is added to your database.

Finally, let's add a page to list all models:

$app->get('/list', function(){
    $widgets = \MyProject\Model\Widget::all();
    foreach ($widgets as $widget) {
        echo "<h1>$widget->name</h1>";
        echo "<p>Serial number: $widget->serial_number</p>";
    }
});

When you visit the URL /list, you should see a list of newly created models.

Thank you for reading this tutorial. You can find this project on GitHub. If you have any questions, feel free to leave a comment below.

Comments