Macros in Laravel (Hexagonal Pattern Design Part 2)

Here I am going to walk through an example of how to use macros and additional views to get rid of logic in our Laravel views.

Let’s pretend we have a form to create a user and one of the fields we can edit is first_name. So we might expect some HTML like below

<div class="control-group">
    <label class="control-label" for="inputFirstName">First Name</label>
    <div class="controls">
        <input type="text" id="inputFirstName" placeholder="First name" value="<?= $user->first_name ?>">
        <?php if ($errors->has('first_name'): ?>
            <div class="errors">
                <?= $errors->first('first_name') ?>  
            </div>
        <?php endif ?>
    </div>
</div>

I can simplify the conditional with a macro called show_message_when.

<div class="control-group">
    <label class="control-label" for="inputFirstName">First Name</label>
    <div class="controls">
        <input type="text" id="inputFirstName" placeholder="First name" value="<?= $user->first_name ?>">
        <?= HTML::show_message_when('first_name', $errors) ?>
    </div>
</div>

And voila! No more logic in our views, it’s all evaluating variables now and all the logic has been extracted away. This makes testing a breeze, as we don’t have to worry about conditional structure in our view - assuming the variables are in place then the system just works.

This is a simple view in the first place but we can always extract out conditional logic from a view. That really only leaves foreach/loop logic, but that doesn’t bother me as much as conditionals so I usually leave those in place if they are simple. You could though create a macro to help with loops if you really wanted to do so. Let’s see another before and after example…

** Before **

<div class="container">
 
    <?php if (!$users): ?>
        <div class="empty row">No users found</div>
    <?php endif ?>
 
    <table class="table">
        <th>
            <td>Id</td>
            <td>First Name</td>
            <td>Last Name</td>
            <td>Score</td>
        </th>
 
        <?php foreach ($users as $user): ?>
            <tr>
                <td class="id"><?= $user->id ?></td>
                <td class="first_name"><?= $user->first_name ?></td>
                <td class="last_name"><?= $user->last_name ?></td>
                <td class="score"><?= $user->rank * $user->proficiency * 
                ($user->updated_at->diffInDays() - $user->created_at->diffInDays()) 
                * 1 / Globals::average_rank() </td> 
            </tr>   
        <?php endforeach ?>
    </table>
 
    <div class="my pagination row">
        <?= $users->links() ?>
    </div>
</div>

After

<div class="container">
 
    <?= HTML::not_found($users, 'No users found!') ?>
 
    <table class="table">
        <th>
            <td>Id</td>
            <td>First Name</td>
            <td>Last Name</td>
            <td>Score</td>
        </th>
 
        <?= HTML::foreach('user', 'user.itemview', $users) ?>
    </table>
 
    <div class="my pagination row">
        <?= $users->links() ?>
    </div>
</div>

And in app/views/user/itemview.php you might have something like this

<tr>
    <td class="id"><?= $user->id ?></td>
    <td class="first_name"><?= $user->first_name ?></td>
    <td class="last_name"><?= $user->last_name ?></td>
    <td class="score"><?= $user->score ?> </td> 
</tr>

So you’re probably wondering what these macros look like? Well, I have made a macros.php gist to share with you guys. Now send me a beer. ^_^

/**
 * HTML::show_message_when('first_name', $errors)
 */
HTML::macro('show_message_when', function show_message_when($name, $errors, $attributes = array())
{
    $attributes_string = "";
    $content_string = "";
 
    $attributes['class'] = isset($attributes['class']) ?: "";
    $attributes['class'] .= " $name";
 
    if ($errors->has($name))
    {
        $attributes['class'] .= " alert";
        $attributes['class'] .= " alert-danger";
        $content_string = $errors->first($name);
    }
 
    foreach ($attributes as $key => $value) {
        $attributes_string = " $key = \"" . $value . "\""; 
    }
 
    return "<div $attributes_string>$content_string</div>";
});
 
 
/**
 * HTML::foreach('user', 'user.itemview', $users)
 */
HTML::macro('foreach', function($name, $view, $items)
{
    var html = "";
 
    foreach ($items as $item)
    {
       html .= View::make($view, [$name => $item]);
    }
 
    return html;
});
 
/**
 * HTML::not_found($users, 'No users found!')
 */
HTML::macro('not_found', function($items, $message = 'None found!')
{
   return '<div class="empty row">' . $message . '</div>';
});

So that about wraps it up. I use macros all the time in my views. You don’t always have to use HTML::macro either. In fact, one macro I use often is just a normal function which I call like active(...) or HTML::active(...) and it allows me to set a class to active when we are visiting the right place.

<li class="<?= active("foobar.index") ?>">
	<a href="<?= route("foobar.index") ?>">
		<i class="fa fa-eye"></i>
		<span class="hidden-sm">My Watchlists</span>
	</a>
</li>

If you didn’t get anything else out of this besides the fact that you can use macros to keep your views clean then I think I can pat myself on the back. Now go, change the world my friend! My next blog will be talking about using presenters to keep views clean in the hexagonal pattern.

post by K.D. on 01/16/2014