Saturday, November 16, 2019

Proper Repository Pattern Design in PHP?

Preface: I'm attempting to use the repository pattern in an MVC architecture with relational databases.
I've recently started learning TDD in PHP, and I'm realizing that my database is coupled much too closely with the rest of my application. I've read about repositories and using an IoC container to "inject" it into my controllers. Very cool stuff. But now have some practical questions about repository design. Consider the follow example.
<?php

class DbUserRepository implements UserRepositoryInterface
{
    protected $db;

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

    public function findAll()
    {
    }

    public function findById($id)
    {
    }

    public function findByName($name)
    {
    }

    public function create($user)
    {
    }

    public function remove($user)
    {
    }

    public function update($user)
    {
    }
}

Issue #1: Too many fields

All of these find methods use a select all fields (SELECT *) approach. However, in my apps, I'm always trying to limit the number of fields I get, as this often adds overhead and slows things down. For those using this pattern, how do you deal with this?

Issue #2: Too many methods

While this class looks nice right now, I know that in a real-world app I need a lot more methods. For example:
  • findAllByNameAndStatus
  • findAllInCountry
  • findAllWithEmailAddressSet
  • findAllByAgeAndGender
  • findAllByAgeAndGenderOrderByAge
  • Etc.
As you can see, there could be a very, very long list of possible methods. And then if you add in the field selection issue above, the problem worsens. In the past I'd normally just put all this logic right in my controller:
<?php

class MyController
{
    public function users()
    {
        $users = User::select('name, email, status')
            ->byCountry('Canada')->orderBy('name')->rows();

        return View::make('users', array('users' => $users));
    }
}
With my repository approach, I don't want to end up with this:
<?php

class MyController
{
    public function users()
    {
        $users = $this->repo->get_first_name_last_name_email_username_status_by_country_order_by_name('Canada');

        return View::make('users', array('users' => $users))
    }

}

Issue #3: Impossible to match an interface

I see the benefit in using interfaces for repositories, so I can swap out my implementation (for testing purposes or other). My understanding of interfaces is that they define a contract that an implementation must follow. This is great until you start adding additional methods to your repositories like findAllInCountry(). Now I need to update my interface to also have this method, otherwise, other implementations may not have it, and that could break my application. By this feels insane...a case of the tail wagging the dog.

Specification Pattern?

This leads me to believe that repository should only have a fixed number of methods (like save(), remove(), find(), findAll(), etc). But then how do I run specific lookups? I've heard of the Specification Pattern, but it seems to me that this only reduces an entire set of records (via IsSatisfiedBy()), which clearly has major performance issues if you're pulling from a database.

I thought I'd take a crack at answering my own question. What follows is just one way of solving the issues 1-3 in my original question.
Disclaimer: I may not always use the right terms when describing patterns or techniques. Sorry for that.

The Goals:

  • Create a complete example of a basic controller for viewing and editing Users.
  • All code must be fully testable and mockable.
  • The controller should have no idea where the data is stored (meaning it can be changed).
  • Example to show a SQL implementation (most common).
  • For maximum performance, controllers should only receive the data they need—no extra fields.
  • Implementation should leverage some type of data mapper for ease of development.
  • Implementation should have the ability to perform complex data lookups.

The Solution

I'm splitting my persistent storage (database) interaction into two categories: R (Read) and CUD (Create, Update, Delete). My experience has been that reads are really what causes an application to slow down. And while data manipulation (CUD) is actually slower, it happens much less frequently, and is therefore much less of a concern.
CUD (Create, Update, Delete) is easy. This will involve working with actual models, which are then passed to my Repositories for persistence. Note, my repositories will still provide a Read method, but simply for object creation, not display. More on that later.
R (Read) is not so easy. No models here, just value objects. Use arrays if you prefer. These objects may represent a single model or a blend of many models, anything really. These are not very interesting on their own, but how they are generated is. I'm using what I'm calling Query Objects.

The Code:

User Model

Let's start simple with our basic user model. Note that there is no ORM extending or database stuff at all. Just pure model glory. Add your getters, setters, validation, whatever.
class User
{
    public $id;
    public $first_name;
    public $last_name;
    public $gender;
    public $email;
    public $password;
}

Repository Interface

Before I create my user repository, I want to create my repository interface. This will define the "contract" that repositories must follow in order to be used by my controller. Remember, my controller will not know where the data is actually stored.
Note that my repositories will only every contain these three methods. The save() method is responsible for both creating and updating users, simply depending on whether or not the user object has an id set.
interface UserRepositoryInterface
{
    public function find($id);
    public function save(User $user);
    public function remove(User $user);
}

SQL Repository Implementation

Now to create my implementation of the interface. As mentioned, my example was going to be with an SQL database. Note the use of a data mapper to prevent having to write repetitive SQL queries.
class SQLUserRepository implements UserRepositoryInterface
{
    protected $db;

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

    public function find($id)
    {
        // Find a record with the id = $id
        // from the 'users' table
        // and return it as a User object
        return $this->db->find($id, 'users', 'User');
    }

    public function save(User $user)
    {
        // Insert or update the $user
        // in the 'users' table
        $this->db->save($user, 'users');
    }

    public function remove(User $user)
    {
        // Remove the $user
        // from the 'users' table
        $this->db->remove($user, 'users');
    }
}

Query Object Interface

Now with CUD (Create, Update, Delete) taken care of by our repository, we can focus on the R (Read). Query objects are simply an encapsulation of some type of data lookup logic. They are not query builders. By abstracting it like our repository we can change it's implementation and test it easier. An example of a Query Object might be an AllUsersQuery or AllActiveUsersQuery, or even MostCommonUserFirstNames.
You may be thinking "can't I just create methods in my repositories for those queries?" Yes, but here is why I'm not doing this:
  • My repositories are meant for working with model objects. In a real world app, why would I ever need to get the password field if I'm looking to list all my users?
  • Repositories are often model specific, yet queries often involve more than one model. So what repository do you put your method in?
  • This keeps my repositories very simple—not an bloated class of methods.
  • All queries are now organized into their own classes.
  • Really, at this point, repositories exist simply to abstract my database layer.
For my example I'll create a query object to lookup "AllUsers". Here is the interface:
interface AllUsersQueryInterface
{
    public function fetch($fields);
}

Query Object Implementation

This is where we can use a data mapper again to help speed up development. Notice that I am allowing one tweak to the returned dataset—the fields. This is about as far as I want to go with manipulating the performed query. Remember, my query objects are not query builders. They simply perform a specific query. However, since I know that I'll probably be using this one a lot, in a number of different situations, I'm giving myself the ability to specify the fields. I never want to return fields I don't need!
class AllUsersQuery implements AllUsersQueryInterface
{
    protected $db;

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

    public function fetch($fields)
    {
        return $this->db->select($fields)->from('users')->orderBy('last_name, first_name')->rows();
    }
}
Before moving on to the controller, I want to show another example to illustrate how powerful this is. Maybe I have a reporting engine and need to create a report for AllOverdueAccounts. This could be tricky with my data mapper, and I may want to write some actual SQL in this situation. No problem, here is what this query object could look like:
class AllOverdueAccountsQuery implements AllOverdueAccountsQueryInterface
{
    protected $db;

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

    public function fetch()
    {
        return $this->db->query($this->sql())->rows();
    }

    public function sql()
    {
        return "SELECT...";
    }
}
This nicely keeps all my logic for this report in one class, and it's easy to test. I can mock it to my hearts content, or even use a different implementation entirely.

The Controller

Now the fun part—bringing all the pieces together. Note that I am using dependency injection. Typically dependencies are injected into the constructor, but I actually prefer to inject them right into my controller methods (routes). This minimizes the controller's object graph, and I actually find it more legible. Note, if you don't like this approach, just use the traditional constructor method.
class UsersController
{
    public function index(AllUsersQueryInterface $query)
    {
        // Fetch user data
        $users = $query->fetch(['first_name', 'last_name', 'email']);

        // Return view
        return Response::view('all_users.php', ['users' => $users]);
    }

    public function add()
    {
        return Response::view('add_user.php');
    }

    public function insert(UserRepositoryInterface $repository)
    {
        // Create new user model
        $user = new User;
        $user->first_name = $_POST['first_name'];
        $user->last_name = $_POST['last_name'];
        $user->gender = $_POST['gender'];
        $user->email = $_POST['email'];

        // Save the new user
        $repository->save($user);

        // Return the id
        return Response::json(['id' => $user->id]);
    }

    public function view(SpecificUserQueryInterface $query, $id)
    {
        // Load user data
        if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
            return Response::notFound();
        }

        // Return view
        return Response::view('view_user.php', ['user' => $user]);
    }

    public function edit(SpecificUserQueryInterface $query, $id)
    {
        // Load user data
        if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
            return Response::notFound();
        }

        // Return view
        return Response::view('edit_user.php', ['user' => $user]);
    }

    public function update(UserRepositoryInterface $repository)
    {
        // Load user model
        if (!$user = $repository->find($id)) {
            return Response::notFound();
        }

        // Update the user
        $user->first_name = $_POST['first_name'];
        $user->last_name = $_POST['last_name'];
        $user->gender = $_POST['gender'];
        $user->email = $_POST['email'];

        // Save the user
        $repository->save($user);

        // Return success
        return true;
    }

    public function delete(UserRepositoryInterface $repository)
    {
        // Load user model
        if (!$user = $repository->find($id)) {
            return Response::notFound();
        }

        // Delete the user
        $repository->delete($user);

        // Return success
        return true;
    }
}

Final Thoughts:

The important things to note here are that when I'm modifying (creating, updating or deleting) entities, I'm working with real model objects, and performing the persistance through my repositories.
However, when I'm displaying (selecting data and sending it to the views) I'm not working with model objects, but rather plain old value objects. I only select the fields I need, and it's designed so I can maximum my data lookup performance.
My repositories stay very clean, and instead this "mess" is organized into my model queries.
I use a data mapper to help with development, as it's just ridiculous to write repetitive SQL for common tasks. However, you absolutely can write SQL where needed (complicated queries, reporting, etc.). And when you do, it's nicely tucked away into a properly named class.
I'd love to hear your take on my approach!

Best Practices for Custom Helpers in Laravel 5

Create a helpers.php file in your app folder and load it up with composer:
"autoload": {
    "classmap": [
        ...
    ],
    "psr-4": {
        "App\\": "app/"
    },
    "files": [
        "app/helpers.php" // <---- ADD THIS
    ]
},
After adding that to your composer.json file, run the following command:
composer dump-autoload

Custom Classes in Laravel 5, the Easy Way

This answer is applicable to general custom classes within Laravel. For a more Blade-specific answer, see Custom Blade Directives in Laravel 5.
Step 1: Create your Helpers (or other custom class) file and give it a matching namespace. Write your class and method:
<?php // Code within app\Helpers\Helper.php

namespace App\Helpers;

class Helper
{
    public static function shout(string $string)
    {
        return strtoupper($string);
    }
}
Step 2: Create an alias:
<?php // Code within config/app.php

    'aliases' => [
     ...
        'Helper' => App\Helpers\Helper::class,
     ...
Step 3: Run composer dump-autoload in the project root
Step 4: Use it in your Blade template:
<!-- Code within resources/views/template.blade.php -->

{!! Helper::shout('this is how to use autoloading correctly!!') !!}
Extra Credit: Use this class anywhere in your Laravel app:
<?php // Code within app/Http/Controllers/SomeController.php

namespace App\Http\Controllers;

use Helper;

class SomeController extends Controller
{

    public function __construct()
    {
        Helper::shout('now i\'m using my helper class in a controller!!');
    }
    ...
https://stackoverflow.com/questions/28290332/best-practices-for-custom-helpers-in-laravel-5

Tuesday, November 12, 2019

Laravel Query Detector

Laravel Two-Step Registration: Optional Fields for Country and Bio

Nowadays, we have a lot of Laravel tutorial about some syntax or package, but I think there’s not enough written with real-life mini-projects, close to real demands of the clients. So will try to write more of these, and this is one of them: imagine a project where you need to have two-step registration process, with some optional fields in the second step. Let’s build it in Laravel.
By default, Laravel registration form has four fields:
Let’s say we have a task to add two more fields: country (dropdown) and Biography (textarea). After successful default registration, user would be redirected to that second step with those two fields, and ability to fill them in, or skip that second step.
Here’s our plan of actions:
  1. Add new fields to User model and migration;
  2. For “country” field we would create a seed for all world’s countries;
  3. Create a GET URL /register-step2 and route/controller/view with form for those two new fields;
  4. Update that form values and redirect to /home;
  5. Add a link to Skip the second step;
  6. Finally, tie it all together to redirect successful registration to that /register-step2.
In reality, I will merge it all into four steps, let’s go.

Step 1. New fields: migrations, seeds and model

We need to add two new fields to the database users table: country_idand biography. But before that, we need to create a new table countriesto have a foreign key to.
So we launch:
php artisan make:migration create_countries_table
And for this we have a “quick hack” – there’s a seeder inside of our QuickAdminPanel generator, that will give us this:
class CreateCountriesTable extends Migration
{
    public function up()
    {
        Schema::create('countries', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('short_code');
            $table->timestamps();
            $table->softDeletes();
        });
    }
}
class CountriesTableSeeder extends Seeder
{
    public function run()
    {
        $countries = [
            [
                'id'         => 1,
                'name'       => 'Afghanistan',
                'short_code' => 'af',
            ],
            [
                'id'         => 2,
                'name'       => 'Albania',
                'short_code' => 'al',
            ],
            [
                'id'         => 3,
                'name'       => 'Algeria',
                'short_code' => 'dz',
            ],

            // ... Other countries

            [
                'id'         => 239,
                'name'       => 'Zambia',
                'short_code' => 'zm',
            ],
            [
                'id'         => 240,
                'name'       => 'Zimbabwe',
                'short_code' => 'zw',
            ],
        ];
        Country::insert($countries);
    }
}
And we add this seeder file into main database/seeds/DatabaseSeeder.php:
class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(CountriesTableSeeder::class);
    }
}
Now we can create a foreign key in users table:
php artisan make:migration add_fields_to_users_table
And here’s the migration code:
public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->unsignedInteger('country_id')->nullable();
        $table->foreign('country_id')->references('id')->on('countries');
        $table->text('biography')->nullable();
    });
}
Finally, we can launch this magic command on our (still empty) database:
php artisan migrate --seed

Registration Step 2: Route/Controller/View

So, we’re building this page now:
Let’s start with routes/web.php:
Route::get('register-step2', 'Auth\RegisterStep2Controller@showForm');
Now, let’s the create the Controller we want, it will be in app/Http/Controllers/Auth/RegisterStep2Controller.php:
namespace App\Http\Controllers\Auth;

use App\Country;
use App\Http\Controllers\Controller;

class RegisterStep2Controller extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function showForm()
    {
        $countries = Country::all();
        return view('auth.register_step2', compact('countries'));
    }
}
As you can see, we add middleware auth inside of Controller’s constructor, so only authenticated users will be able to access that step 2 – immediately after registration.
Also, if you remember, one of the fields will be Countries list, so we need to pass it from Controller.
Now, let’s create a Blade file – for this, we will just copy-paste register.blade.php and change the input fields. Here’s the result resources/views/auth/register_step2.blade.php:
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register Step 2 (optional)') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('register.step2') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Country') }}</label>

                            <div class="col-md-6">
                                <select name="country_id" class="form-control @error('country_id') is-invalid @enderror">
                                    <option value="">-- {{ __('choose your country') }} --</option>
                                    @foreach ($countries as $country)
                                        <option value="{{ $country->id }}">{{ $country->name }}</option>
                                    @endforeach
                                </select>

                                @error('country_id')
                                <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Biography') }}</label>

                            <div class="col-md-6">
                                <textarea class="form-control @error('biography') is-invalid @enderror" name="biography">{{ old('biography') }}</textarea>

                                @error('biography')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Finish Registration') }}
                                </button>
                                <br /><br />
                                <a href="{{ route('home') }}">Skip for now</a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
As you can see, we’re adding “Skip for now” link to /home route. Also, we’re referencing the POST action to the route name register.step2 that doesn’t exist yet. This is our next step.

Step 3. Update the Fields

This is pretty simple, we just add a new method to our new Controller, and point to it in Routes. Remember, in the Blade file above, we already referenced it:
<form method="POST" action="{{ route('register.step2') }}">
So, we need to add a new line in routes/web.php:
Route::post('register-step2', 'Auth\RegisterStep2Controller@postForm')
  ->name('register.step2');
Our postForm() method will be as simple as that:
use Illuminate\Http\Request;

class RegisterStep2Controller extends Controller
{

    // ... other methods

    public function postForm(Request $request)
    {
        auth()->user()->update($request->only(['biography', 'country_id']));
        return redirect()->route('home');
    }

}
To make this work, we also need to make those two new fields fillable, in app/User.php – just add them into already existing array:
class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password', 'country_id', 'biography'
    ];
And, after we login/register, go to /register-step2 URL, fill in the form – we get success in the database:

Step 4. Redirect Registration to Step 2

Final step is probably the most simple one. By default, successful Laravel registration redirects user to /home URL, it is set in app/Http/Controllers/Auth/RegisterController.php:
class RegisterController extends Controller
{
    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    // ...

}
So, all we need to do is change the value to this:
protected $redirectTo = '/register-step2';
And, that’s it, our tutorial is done!