<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Laravel 4 Tutorials - Medium]]></title>
        <description><![CDATA[Things you can build with Laravel 4. - Medium]]></description>
        <link>https://medium.com/laravel-4?source=rss----ec6a040a914---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*fN4tpdLQxM-t8k64.png</url>
            <title>Laravel 4 Tutorials - Medium</title>
            <link>https://medium.com/laravel-4?source=rss----ec6a040a914---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 31 May 2026 22:19:09 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/laravel-4" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Where Is All The Content?]]></title>
            <link>https://medium.com/laravel-4/where-is-all-the-content-4f2359104475?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/4f2359104475</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Tue, 26 May 2015 05:11:43 GMT</pubDate>
            <atom:updated>2015-05-26T05:11:43.875Z</atom:updated>
            <content:encoded><![CDATA[<p>You may have noticed a shortage of content here. From now on, the posts I write about Laravel will be over in <a href="https://medium.com/laravel-5-tutorials">https://medium.com/laravel-5-tutorials</a>.</p><p>Happy learning!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f2359104475" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/where-is-all-the-content-4f2359104475">Where Is All The Content?</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Forge + SSL]]></title>
            <link>https://medium.com/laravel-4/forge-ssl-66b4252db7d8?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/66b4252db7d8</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Mon, 02 Jun 2014 10:56:36 GMT</pubDate>
            <atom:updated>2015-03-23T02:28:13.677Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><p>Laravel Forge is awesome. It’s never been easier to ship Laravel applications, even with zero sysadmin knowledge. It’s even easy to set up SSL for your site. That is what we’re going to talk about, in this article.</p><h3>Disclaimer</h3><p>Forge is very dynamic. It’s new, and Taylor is adding features quicker than people can document. That makes this article subject to changes in Forge. Some of it may no longer be needed, by the time you encounter it. I will try to keep it updated though. Leave comments, or hit me up on Twitter, if you spot any changes.</p><h3>Step 1: Provisioning Your Application</h3><p>To get the ball rolling, you need to provision your application.</p><ol><li>Get an account at <a href="https://forge.laravel.com/"><strong>https://forge.laravel.com</strong></a></li><li>Add your server provider credentials, at <a href="https://forge.laravel.com/user/profile"><strong>https://forge.laravel.com/user/profile</strong></a></li><li>Create a new server</li><li>Delete the default site</li><li>Create a new site</li></ol><p>For <a href="https://rebuildinglaravel.com/"><strong>https://rebuildinglaravel.com</strong></a> I used Digital Ocean and Github. It’s very important that you delete the default site (which is created on new servers). When you have a certificate, you’ll want to redirect non-SSL requests to the main SSL landing page. If you used the default site to host your application, the redirect will not work.</p><h3>Step 2: Request An SSL Certificate</h3><p>Getting SSL certificates requires generating what is called a Certificate Signing Request (CSR), and sending this to a certificate authority. The authority creates the corresponding SSL certificate, and that’s what you then install on the server.</p><p>Head over to the SSL Certificates tab, on the site edit page (something like <strong>https://forge.laravel.com/servers/[server id]/sites/[site id]</strong>) and create a Signing Request.</p><blockquote>The values you enter should be specific to you. Authorities aren’t usually picky about this data, but use the best information you can.</blockquote><p>Once the signing request is created, you can view it by clicking <strong>View CSR</strong>. The data between <strong>BEGIN CERTIFICATE REQUEST</strong> and <strong>END CERTIFICATE REQUEST</strong> is the signing request, but you will want to copy all the text (including the header and footer) for when you need to submit it to the authority.</p><p>Next, you will need to decide which authority to go with. I decided to apply for an open-source certificate, with <a href="https://www.globalsign.com/"><strong>https://www.globalsign.com</strong></a>. You can find application details at <a href="https://www.globalsign.com/ssl/ssl-open-source"><strong>https://www.globalsign.com/ssl/ssl-open-source</strong></a>. They were kind enough to grant me a certificate, for <a href="https://rebuildinglaravel.com/"><strong>https://rebuildinglaravel.com</strong></a>.</p><p>Once you know who you’re going with, you’ll need to follow their processes for submitting the CSR. They’ll take a few days to approve the certificate, and probably charge a small fee (unless they’re nice like Global Sign was).</p><h3>Step 3: Installing the CSR</h3><p>Once the certificate has been approved, you’ll get an email (or whatever method the authority uses), and the certificate will look similar to the CSR. Go back to where you created the CSR, and click <strong>Install Certificate</strong>.</p><p>When you’re ready to switch the application over to SSL, click <strong>Activate Certificate</strong>.</p><h3>Step 4: Redirects</h3><p>If you want to support <strong>https://example.com</strong> as well as <strong>https://www.example.com</strong> formats, you’ll need to add some redirects to the Nginx config files. <strong>If not, skip to the next step</strong>.</p><p>Currently, adding multiple domain names to a single site (in Forge) requires some manual configuration. When you provisioned the new server, you should have received an email; containing login details for the server.</p><p>Use these to log in:</p><pre>$ ssh forge@123.123.123.123<br> <br>The authenticity of host &#39;example.com (123.123.123.123)&#39; can&#39;t be established.<br>RSA key fingerprint is a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1.<br>Are you sure you want to continue connecting (yes/no)? yes<br>Warning: Permanently added &#39;example.com,123.123.123.123&#39; (RSA) to the list of known hosts.<br> <br>forge@example.com&#39;s password:</pre><p>Once you input the correct password, you should see something resembling:</p><pre>Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.13.0-24-generic x86_64)<br> <br> * Documentation: https://help.ubuntu.com/<br> <br> System information as of Mon Jun 2 12:19:05 SAST 2014<br> <br> System load: 0.0 Processes: 82<br> Usage of /: 12.0% of 19.56GB Users logged in: 0<br> Memory usage: 27% IP address for eth0: 123.123.123.123<br> Swap usage: 0% IP address for eth1: 123.123.123.123<br> <br> Graph this data and manage this system at:<br> https://landscape.canonical.com/<br> <br>Last login: Mon Jun 2 09:52:25 2014 from 123.123.123.123</pre><blockquote>Obviously, all the IP addresses and domains names will be specific to your context. Please don’t ask me why your IP (123.123.123.123) isn’t working. I’ll throw a trout at you!</blockquote><p>Thankfully Forge boxes already have an assortment of Linux utilities installed. Edit the configuration file (for your application), similarly to:</p><pre>$ sudo vim /etc/nginx/sites-available/example.com</pre><p>With the certificate installed (and activated), you should see something like:</p><pre>server {<br>  listen 80;<br>  server_name example.com;<br>  return 301 https://example.com$request_uri;<br>}<br> <br>server {<br>  listen 443 ssl;<br>  server_name example.com;<br>  root /home/forge/example.com/public;</pre><p>Add the <strong>www</strong> version inline:</p><pre>server {<br>  listen 80;<br>  server_name example.com <strong>www.example.com</strong>;<br>  return 301 https://example.com$request_uri;<br>}<br> <br>server {<br>  listen 443 ssl;<br>  server_name example.com <strong>www.example.com</strong>;<br>  root /home/forge/example.com/public;</pre><p>These changes tell the server to respond to both formats, and to redirect both formats from <strong>http</strong> to <strong>https</strong>.</p><p>Depending on the kind of certificate you got, you may also need to redirect from <strong>www</strong> URLs to URLs without. In that case, add the following:</p><pre>server {<br>  listen 443 ssl;<br>  server_name example.com www.example.com;<br>  root /home/forge/example.com/public;<br> <br>  <strong>if ($host = &#39;www.example.com&#39;) {<br>    rewrite ^/(.*)$ https://example.com/$1 permanent;<br>  }</strong></pre><h3>Step 5: Back Up Keys</h3><p>Certificates are generated according to a private key file. If you want to move the SSL certificate to another machine, and you don’t have the key file, you’re out of luck!</p><p>Just below the redirects we added, you should see a few lines resembling:</p><pre># FORGE SSL (DO NOT REMOVE!)<br>ssl on;<br>ssl_certificate /etc/nginx/ssl/example.com/123/server.crt;<br>ssl_certificate_key /etc/nginx/ssl/example.com/123/server.key;</pre><p>A quick <strong>ls</strong> reveals three files in that folder:</p><ol><li>The original CSR</li><li>The approved certificate</li><li>A private key (used to generate the CSR, and checked against the approved certificate).</li></ol><p>Back these three files up somewhere, so you can get to them in an emergency. An easy way to do this (without much bash-foo), is:</p><pre>$ cat /etc/nginx/ssl/example.com/123/*</pre><p>This will output all three files, and you can copy/paste them to somewhere secure. Don’t go spreading these around.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=66b4252db7d8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/forge-ssl-66b4252db7d8">Forge + SSL</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Migrations For The Conscientious]]></title>
            <link>https://medium.com/laravel-4/migrations-for-the-conscientious-6e75f99cdb0?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/6e75f99cdb0</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Tue, 29 Apr 2014 10:23:43 GMT</pubDate>
            <atom:updated>2014-04-29T10:23:40.662Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eAIMNhB-gzzSdEugbTOmiQ.png" /></figure><p>I learned much from reading Taylor Otwell’s brief, yet insightful, <a href="https://leanpub.com/laravel"><strong>Laravel: From Apprentice To Artisan</strong></a>. The parts dealing with domain-specific folders were echoes of a talk by Robert C. Martin, <a href="https://www.youtube.com/watch?v=WpkDN78P884"><strong>Architecture The Lost Years</strong></a>.</p><p>The idea is that your application’s folder structure shouldn’t be defined in terms of the delivery mechanism that is HTTP. It shouldn’t be defined in terms of the organisational model that is Model-View-Controller. It should be defined in terms of the application structure and behavior.</p><p>So I’ve recently been thinking a lot about this idea, and trying to structure code which fits with it. Naturally most of my Laravel application code is easily relocated and brought under the applicable namespaces. Migrations, however…</p><p>The problem with migrations is that their names are significant for their behavior, to the point where they won’t function if they aren’t named correctly. This is not uncommon in migration schemes owing to the need for chronological ordering. Migrations need to happen in the order they were created. They need to unhappen in the the opposite order.</p><p>Laravel makes this painfully clear when trying to namespace migration classes. Sure, you can change their filesystem location, and use the <strong>path</strong> argument (or the <strong>bench</strong> argument, or the <strong>package</strong> argument). These are designed to allow running migrations from anywhere. What they don’t cater for is when those migrations belong to a namespace (which is not the global namespace).</p><p>You can run <strong>composer dump-autoload</strong> and use as many <strong>path</strong> arguments as you like; if your migrations are in a namespace then Laravel will not migrate them.</p><p>It’s not all doom and gloom though. Because Laravel is built on a strong IoC foundation, it’s easy to subclass and substitute a migration handler class which deals with this natural (and annoying) weakness. Observe…</p><p>The first step is to subclass and substitute the <strong>Migrator</strong> class. You can find the base class at <strong>Illuminate\Database\Migrations\Migrator</strong>. It’s registered in <strong>Illuminate\Database\MigrationServiceProvider</strong> with the key <strong>migrator</strong>, so it’s easy to substitute in out custom code:</p><pre>&lt;?php<br> <br>namespace Acme;<br> <br>use Illuminate\Database\Migrations\Migrator as Base;<br> <br>class Migrator<br>  extends Base<br>{<br>  /**<br>   * Resolve a migration instance from a file.<br>   *<br>   * <a href="http://twitter.com/param">@param</a> string $file<br>   * <a href="http://twitter.com/return">@return</a> object<br>   */<br>  public function resolve($file)<br>  {<br>    $file = implode(&quot;_&quot;, array_slice(explode(&quot;_&quot;, $file), 4));<br>  <br>    $class = &quot;Acme\\Migration\\&quot; . studly_case($file);<br>  <br>    return new $class;<br>  }<br>}</pre><p>The <strong>resolve()</strong> method is almost exactly the same as in the base <strong>Migrator</strong> class, except that we’re now able to prefix the resolved class name with a namespace. Now we just have to swap this for the base <strong>Migrator</strong>:</p><pre>&lt;?php<br> <br>namespace Acme;<br> <br>use Illuminate\Support\ServiceProvider;<br> <br>class AcmeServiceProvider<br>  extends ServiceProvider<br>{<br>  public function register()<br>  {<br>    $this-&gt;app-&gt;bindShared(<br>      &quot;migrator&quot;,<br>      function () {<br>        return new Migrator(<br>          $this-&gt;app-&gt;make(&quot;migration.repository&quot;),<br>          $this-&gt;app-&gt;make(&quot;db&quot;),<br>          $this-&gt;app-&gt;make(&quot;files&quot;)<br>        );<br>      }<br>    );<br>  }<br>}</pre><p>This is exactly the same <strong>bindShared()</strong> call that happened in the base service provider. I’ve just cleaned it up a little…</p><blockquote>We will still have to use the <strong>path</strong> argument, as Laravel expects the migration classes to be in <strong>app/database/migrations</strong>. We will still need to have the timestamps in the migration file names, as Laravel needs them to work out the running order.</blockquote><p>Instead of seeing class not found exceptions, it’s business as usual:</p><pre>❯ php artisan migrate --path=&quot;app/Acme/Migration&quot;</pre><blockquote>If you need more info on running package migrations (the traditional approach), you can find it at: <a href="http://laravel.com/docs/packages#package-migrations"><strong>http://laravel.com/docs/packages#package-migrations</strong></a>.</blockquote><blockquote>If you need more info on how to write migrations, you can find it at: <a href="http://laravel.com/docs/migrations"><strong>http://laravel.com/docs/migrations</strong></a>.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6e75f99cdb0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/migrations-for-the-conscientious-6e75f99cdb0">Migrations For The Conscientious</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel’s URL::to() vs URL::asset()]]></title>
            <link>https://medium.com/@zwacky/laravels-url-to-vs-url-asset-fd427ed6f7ef?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/fd427ed6f7ef</guid>
            <dc:creator><![CDATA[Simon Wicki]]></dc:creator>
            <pubDate>Mon, 28 Apr 2014 11:29:35 GMT</pubDate>
            <atom:updated>2020-11-26T07:53:11.782Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/desat/multiply/grey/60/overlay/grey/1*qQhvMYTTdqEo50gDBSS--g.jpeg" /></figure><h4>Should I bother using asset() over to()?</h4><p>Short answer: No, not really.</p><p>You want to have the following code:</p><pre>&lt;script src=&quot;js/bootstrap.js&quot;&gt;&lt;/script&gt;</pre><p>Now you can achieve this using blade syntax:</p><pre>// using URL::to()<br>&lt;script src=&quot;{{ URL::to(&#39;js/bootstrap.js&#39;) }}&quot;&gt;&lt;/script&gt;<br><br>// using URL::asset()<br>&lt;script src=&quot;{{ URL::asset(&#39;js/bootstrap.js&#39;) }}&quot;&gt;&lt;/script&gt;</pre><h3>URL::to()</h3><p>The method is <a href="http://laravel.com/api/source-class-Illuminate.Routing.UrlGenerator.html#76-98">implemented</a> like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/696/1*xD1x7OeXU5NNGwfKWsvpcw.png" /></figure><h3>URL::asset()</h3><p>The method is <a href="http://laravel.com/api/source-class-Illuminate.Routing.UrlGenerator.html#112-129">implemented</a> like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/706/1*GpHcueYgmqZiFQDF4k345A.png" /></figure><h3>Difference</h3><p>Both methods get the job done.</p><ul><li><strong>URL::to()</strong> additionally encodes the segments of the passed url with rawurlencode</li><li><strong>URL::asset()</strong> removes index.php from the path (which shouldn’t be in first place)</li></ul><p>So you get more utility by using <strong>URL::to()</strong>, but also takes longer to execute (approx. 25% more than URL::asset()). But it’s <strong>strongly</strong> neglectible, since calling 100 times URL::to() takes 0.003523 ms.</p><h3>Alternatives</h3><p>If you’re using blade, you can use the following to easily output style and script tags.</p><pre>{{ HTML::script(&#39;js/bootstrap.js&#39;) }}<br>{{ HTML::style(&#39;css/bootstrap.min.css&#39;) }}</pre><p>Or using any of Asset Management packages like <a href="https://github.com/orchestral/asset">orchestra/asset</a>.</p><p><a href="https://wicki.io">Simon Wicki</a> is a Frontend Developer in Berlin. Passionate and fluent in Vue, Angular, React and Ionic. I love Hybrid Apps and books.</p><p><strong>👉 </strong><a href="https://twitter.com/zwacky"><strong>Join me on Twitter to follow my latest updates.</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fd427ed6f7ef" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 4 Controller Testing]]></title>
            <link>https://medium.com/laravel-4/laravel-4-controller-testing-48414f4782d0?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/48414f4782d0</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Sat, 12 Apr 2014 19:04:13 GMT</pubDate>
            <atom:updated>2014-04-19T08:36:42.328Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A Comprehensive Tutorial</h4><p>Testing is a hot topic these days. Everyone knows that having tests is a good thing, but often they’re either “too busy” to write them, or they just don’t know how.</p><p>Even if you do write tests, it may not always be clear what the best way is to prepare for them. Perhaps you’re used to writing them against a particular framework (which I’m assuming is not Laravel 4) or you tend to write fewer when you can’t figure out how exactly to test a particular section of your application.</p><p>I have recently spent much time writing tests, and learning spades about how to write them. I have Jeffrey Way to thank, both for <a href="https://leanpub.com/laravel-testing-decoded"><strong>Laravel: Testing Decoded</strong></a> and <a href="https://laracasts.com/"><strong>Laracasts.com</strong></a>. These resources have taught me just about everything I know about unit/functional testing.</p><p>This article is about some of the practical things you can do to make your code easier to test, and how you would go about writing functional/unit tests against that code. If you take nothing else away, after reading this, you should be subscribed to <a href="https://laracasts.com/"><strong>Laracasts.com</strong></a> and you should read <a href="https://leanpub.com/laravel-testing-decoded"><strong>Laravel: Testing Decoded</strong></a>.</p><p>Since there’s already so much in the way of testing, I thought it would be better just to focus on the subject of how to write testable controllers, and then how to test that they are doing what they are supposed to be doing.</p><p>This tutorial is also different from <a href="https://medium.com/laravel-4"><strong>my others</strong></a> because it doesn’t build a single project, but rather demonstrates general principles which can be used for them all.</p><blockquote>While much effort has been spent in the pursuit of accuracy; there’s a good chance you could stumble across a curly quote in a code listing, or some other egregious errata. Please make a note and I will fix where needed.</blockquote><blockquote>I have also uploaded this code to Github. You need simply follow the configuration instructions in this tutorial, after downloading the source code, and the application should run fine.</blockquote><blockquote>This assumes, of course, that you know how to do that sort of thing. If not; this shouldn’t be the first place you learn about making PHP applications.</blockquote><blockquote><a href="https://github.com/formativ/tutorial-laravel-4-testing"><strong>https://github.com/formativ/tutorial-laravel-4-testing</strong></a></blockquote><blockquote>If you spot differences between this tutorial and that source code, please raise it here or as a GitHub issue. Your help is greatly appreciated.</blockquote><h3>Installing Dependencies</h3><p>For this chapter, we’re going to be using PHPUnit and Mockery. Both of these libraries are used in testing, and can be installed at the same time, by running the following commands:</p><pre>❯ composer require —dev &quot;phpunit/phpunit:4.0.*&quot;</pre><pre>./composer.json has been updated<br>Loading composer repositories with package information<br>...</pre><pre>❯ composer require —dev &quot;mockery/mockery:0.9.*&quot;</pre><pre>./composer.json has been updated<br>Loading composer repositories with package information<br>...</pre><p>We’ll get into the specifics of each later, but this should at least install them and add them to the <strong>require-dev</strong> section of your <strong>composer.json</strong> file.</p><h3>Unit vs. Functional vs. Acceptance</h3><p>There are many kinds of tests. There are three which we are going to look at:</p><ol><li>Unit</li><li>Functional</li><li>Acceptance</li></ol><h4>Unit Tests</h4><p>Unit tests are tests which cover individual functions or methods. They are meant to run the functions or methods in complete isolation, with no dependencies or side-effects.</p><blockquote>You can find a boring description at: <a href="http://en.wikipedia.org/wiki/Unit_testing"><strong>http://en.wikipedia.org/wiki/Unit_testing</strong></a>.</blockquote><h4>Functional Tests</h4><p>Functional tests are tests which concentrate on the input given to some function or method, and the output returned. They don’t care about isolation or state.</p><blockquote>You can find a boring description at: <a href="http://en.wikipedia.org/wiki/Functional_testing"><strong>http://en.wikipedia.org/wiki/Functional_testing</strong></a>.</blockquote><h4>Acceptance Tests</h4><p>Acceptance tests are test which look at a much broader range of functionality. These are often called end-to-end tests because they are concerned with the correct functionality over a range of functions, methods classes etc.</p><blockquote>You can find a boring description at: <a href="http://en.wikipedia.org/wiki/Acceptance_testing"><strong>http://en.wikipedia.org/wiki/Acceptance_testing</strong></a>.</blockquote><h3>Am I Writing Unit Or Functional Tests?</h3><p>When it comes to writing tests in Laravel 4, developers often think they are writing unit tests when they are actually writing functional tests. The difference is small but significant.</p><p>Laravel provides a set of test classes (a <strong>TestCase</strong> class, and an <strong>ExampleTest</strong> class). If you base your tests off of these, you are probably writing functional tests. The <strong>TestCase</strong> class actually initialises the Laravel 4 framework. If your code depends on that being the case (accessing the aliases, service providers etc.) then your tests aren’t isolated. They have dependencies and they need to be run in order.</p><p>When your tests only require the underlying PHPUnit or PHPSpec classes, then you may be writing unit tests.</p><p>How does this affect us? Well — if your goal is to write functional tests, and you have decent test coverage then you’re doing ok. But if you want to write true unit tests, then you need to pay attention to how your code is constructed. If you’re using the handy aliases, which Laravel provides, then writing unit tests may be tough.</p><p>That should be enough theory to get us started. Let’s take a look at some code…</p><h3>Fat Controllers</h3><p>It’s not uncommon to find controllers with actions resembling the following:</p><pre>public function store()<br>{<br>  $validator = Validator::make(Input::all(), [<br>    &quot;title&quot;    =&gt; &quot;required|max:50&quot;,<br>    &quot;subtitle&quot; =&gt; &quot;required|max:100&quot;,<br>    &quot;body&quot;     =&gt; &quot;required&quot;,<br>    &quot;author&quot;   =&gt; &quot;required|exists:authors&quot;<br>  ]);<br>  <br>  if ($validator-&gt;passes()) {<br>    Posts::create([<br>      &quot;title&quot;     =&gt; Input::get(&quot;title&quot;),<br>      &quot;subtitle&quot;  =&gt; Input::get(&quot;subtitle&quot;),<br>      &quot;body&quot;      =&gt; Input::get(&quot;body&quot;),<br>      &quot;author_id&quot; =&gt; Input::get(&quot;author&quot;),<br>      &quot;slug&quot;      =&gt; Str::slug(Input::get(&quot;title&quot;))<br>    ]);<br>  <br>    Mail::send(&quot;emails.post&quot;, Input::all(), function($email) {<br>      $email<br>        -&gt;to(&quot;<a href="mailto:cgpitt@gmail.com">cgpitt@gmail.com</a>&quot;, &quot;Chris&quot;)<br>        -&gt;subject(&quot;New post&quot;);<br>    });<br>  <br>    return Redirect::route(&quot;posts.index&quot;);<br>  }<br>  <br>  return Redirect::back()<br>    -&gt;withErrors($validator)<br>    -&gt;withInput();<br>}</pre><blockquote>This was extracted from <strong>app/controllers/PostController.php</strong>. I generated the file with the <strong>controller:make</strong> command.</blockquote><p>This is how we first learn to use MVC frameworks, but there comes a point where we are familiar with how they work, and need to start writing tests. Testing this action would be a nightmare. Fortunately, there are a few improvements we can make.</p><h3>Service Providers</h3><p>We’ve used service providers to organise and package our code. Now we’re going to use them to help us thin our controllers out. Create a new service provider, resembling the following:</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>use Illuminate\Support\ServiceProvider;<br> <br>class PostServiceProvider<br>extends ServiceProvider<br>{<br>  protected $defer = true;<br>  <br>  public function register()<br>  {<br>    $this-&gt;app-&gt;bind(<br>      &quot;Formativ\\PostRepositoryInterface&quot;,<br>      &quot;Formativ\\PostRepository&quot;<br>    );<br>  <br>    $this-&gt;app-&gt;bind(<br>      &quot;Formativ\\PostValidatorInterface&quot;,<br>      &quot;Formativ\\PostValidator&quot;<br>    );<br>  <br>    $this-&gt;app-&gt;bind(<br>      &quot;Formativ\\PostMailerInterface&quot;,<br>      &quot;Formativ\\PostMailer&quot;<br>    );<br>  }<br>  <br>  public function provides()<br>  {<br>    return [<br>      &quot;Formativ\\PostRepositoryInterface&quot;,<br>      &quot;Formativ\\ValidatorInterface&quot;,<br>      &quot;Formativ\\MailerInterface&quot;<br>    ];<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostServiceProvider.php</strong>.</blockquote><blockquote>You will also need to load the <strong>Formativ</strong> namespace through the <strong>composer.json</strong> file. You can do that by using either PSR specification, or even through a classmap. More info on that at: <a href="https://getcomposer.org/doc/01-basic-usage.md#autoloading"><strong>https://getcomposer.org/doc/01-basic-usage.md#autoloading</strong></a>.</blockquote><blockquote>You can find out more about making service providers at: <a href="http://laravel.com/docs/packages"><strong>http://laravel.com/docs/packages</strong></a>.</blockquote><p>This does a couple of important things:</p><ol><li>Interfaces are connected to concrete implementations, so that we can type-hint the interfaces in our controller, and they will be resolved automatically, with the Laravel IoC container.</li><li>We specify which interfaces are provided (in the <strong>provides()</strong> method) so that we can defer the loading of this service provider until the concrete implementations are called.</li></ol><p>We need to define the interfaces and concrete implementations:</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>interface PostRepositoryInterface<br>{<br>  public function all(array $modifiers);<br>  public function first(array $modifiers);<br>  public function insert(array $data);<br>  public function update(array $data, array $modifiers);<br>  public function delete(array $modifiers);<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostRepositoryInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>class PostRepository implements PostRepositoryInterface<br>{<br>  public function all(array $modifiers)<br>  {<br>    // return all the posts filtered by $modifiers...<br>  }<br>  <br>  public function first(array $modifiers)<br>  {<br>    // return the first post filtered by $modifiers...<br>  }<br>  <br>  public function insert(array $data)<br>  {<br>    // insert posts with $data...<br>  }<br>  <br>  public function update(array $data, array $modifiers)<br>  {<br>    // update posts filtered by $modifiers, with $data...<br>  }<br>  <br>  public function delete(array $modifiers)<br>  {<br>    // delete posts filtered by $modifiers...<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostRepository.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>interface PostValidatorInterface<br>{<br>  public function passes($event);<br>  public function messages($event);<br>  public function on($event);<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostValidatorInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>class PostValidator implements PostValidatorInterface<br>{<br>  public function passes($event)<br>  {<br>    // validate the event instance...<br>  }<br>  <br>  public function messages($event)<br>  {<br>    // fetch the error messages for the event instance...<br>  }<br>  <br>  public function on($event)<br>  {<br>    // set up the event instance and return it for chaining...<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostValidator.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>interface PostMailerInterface<br>{<br>  public function send($to, $view, $data);<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostMailerInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>class PostMailer implements PostMailerInterface<br>{<br>  public function send($to, $view, $data)<br>  {<br>    // send an email about the post...<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostMailer.php</strong>.</blockquote><h3>Dependency Injection</h3><p>With all of these interfaces and concrete implementations in place, we can simply type-hint the interfaces in our controller. This is essentially dependency injection. We don’t create or use dependencies in our controller — rather they are passed in when the controller is instantiated.</p><blockquote>The dependencies are resolved automatically, via the IoC container and reflection.</blockquote><p>This makes the controller thinner, and helps us break the logic up into a number of smaller, easier-to-test classes:</p><pre>&lt;?php<br> <br>use Formativ\PostRepositoryInterface;<br>use Formativ\PostValidatorInterface;<br>use Formativ\PostMailerInterface;<br>use Illuminate\Support\Facades\Response;<br> <br>class PostController<br>extends BaseController<br>{<br>  public function __construct(<br>    PostRepositoryInterface $repository,<br>    PostValidatorInterface $validator,<br>    PostMailerInterface $mailer,<br>    Response $response<br>  )<br>  {<br>    $this-&gt;repository = $repository;<br>    $this-&gt;validator  = $validator;<br>    $this-&gt;mailer     = $mailer;<br>    $this-&gt;response   = $response;<br>  }<br>  <br>  public function store()<br>  {<br>    if ($this-&gt;validator-&gt;passes(&quot;store&quot;))) {<br>      $this-&gt;repository-&gt;insert([<br>        &quot;title&quot;     =&gt; Input::get(&quot;title&quot;),<br>        &quot;subtitle&quot;  =&gt; Input::get(&quot;subtitle&quot;),<br>        &quot;body&quot;      =&gt; Input::get(&quot;body&quot;),<br>        &quot;author_id&quot; =&gt; Input::get(&quot;author&quot;),<br>        &quot;slug&quot;      =&gt; Str::slug(Input::get(&quot;title&quot;))<br>      ]);<br>       <br>      $this-&gt;mailer-&gt;send(&quot;<a href="mailto:cgpitt@gmail.com">cgpitt@gmail.com</a>&quot;, &quot;emails.post&quot;);<br>    <br>      return $this-&gt;response<br>        -&gt;route(&quot;posts.index&quot;)<br>        -&gt;with(&quot;success&quot;, true);<br>    }<br>  <br>    return $this-&gt;response<br>      -&gt;back()<br>      -&gt;withErrors($this-&gt;validator-&gt;messages(&quot;store&quot;))<br>      -&gt;withInput();<br> }</pre><blockquote>This was extracted from <strong>app/controllers/PostController.php</strong>.</blockquote><p>Another thing you can do, to further modularise your logic, is to dispatch events at critical points in execution:</p><pre>&lt;?php<br> <br>use Formativ\PostRepositoryInterface;<br>use Formativ\PostValidatorInterface;<br>use Formativ\PostMailerInterface;<br>use Illuminate\Support\Facades\Response;<br>use Illuminate\Events\Dispatcher;<br> <br>class PostController<br>extends BaseController<br>{<br>  public function __construct(<br>    PostRepositoryInterface $repository,<br>    PostValidatorInterface $validator,<br>    PostMailerInterface $mailer,<br>    Response $response,<br>    Dispatcher $dispatcher<br>  )<br>  {<br>    $this-&gt;repository = $repository;<br>    $this-&gt;validator  = $validator;<br>    $this-&gt;mailer     = $mailer;<br>    $this-&gt;response   = $response;<br>    $this-&gt;dispatcher = $dispatcher;<br>   <br>    $this-&gt;dispatcher-&gt;listen(<br>      &quot;post.store&quot;,<br>      [$this-&gt;repository, &quot;insert&quot;]<br>    );<br>     <br>    $this-&gt;dispatcher-&gt;listen(<br>      &quot;post.store&quot;,<br>      [$this-&gt;mailer, &quot;send&quot;]<br>    );<br>  }<br>  <br>  public function store()<br>  {<br>    if ($this-&gt;validator-&gt;passes(&quot;store&quot;)) {<br>      $this-&gt;dispatcher-&gt;fire(&quot;post.store&quot;);<br>    <br>      return $this-&gt;response<br>        -&gt;route(&quot;posts.index&quot;)<br>        -&gt;with(&quot;success&quot;, true);<br>    }<br>    <br>    return $this-&gt;response<br>      -&gt;back()<br>      -&gt;withErrors($this-&gt;validator-&gt;messages(&quot;store&quot;))<br>      -&gt;withInput();<br>  }</pre><blockquote>This was extracted from <strong>app/controllers/PostController.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>use Illuminate\Http\Request;<br>use Str;<br> <br>class PostRepository implements PostRepositoryInterface<br>{<br>  public function __construct(Request $request)<br>  {<br>    $this-&gt;request = $request;<br>  }<br>  <br>  public function insert()<br>  {<br>    $data = [<br>      &quot;title&quot;     =&gt; $this-&gt;request-&gt;get(&quot;title&quot;),<br>      &quot;subtitle&quot;  =&gt; $this-&gt;request-&gt;get(&quot;subtitle&quot;),<br>      &quot;body&quot;      =&gt; $this-&gt;request-&gt;get(&quot;body&quot;),<br>      &quot;author_id&quot; =&gt; $this-&gt;request-&gt;get(&quot;author&quot;),<br>      &quot;slug&quot;      =&gt; Str::slug($this-&gt;request-&gt;get(&quot;title&quot;))<br>    ];<br>  <br>    // insert posts with $data...<br>  }</pre><blockquote>This was extracted from <strong>app/Formativ/PostRepository.php</strong>.</blockquote><p>Using this approach, you can delegate method calls based on events, rather than explicit method calls and variable manipulation.</p><blockquote>There are loads of different ways to use events, so you should definitely check out the official docs at: <a href="http://laravel.com/docs/events"><strong>http://laravel.com/docs/events</strong></a>.</blockquote><h3>This Isn’t Testing!</h3><p>If you’re wondering how this is related to testing, consider how you would have tested the original controller code. There are many different responsibilities, and places for errors to emerge.</p><p>Splitting off your logic into classes of single responsibility is a good thing. Generally following SOLID principles is a good thing. The smaller your classes are, the fewer things each of them do, the easier they are to test.</p><p>So how would we write tests for these new classes? Let’s begin with one of the concrete implementations:</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>class PostMailerTest<br>extends TestCase<br>{<br>  public function testSend()<br>  {<br>    // ...your test here<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/tests/Formativ/PostMailerTest.php</strong>.</blockquote><p>In order for this first test case to run, we’ll need to set up a phpunit config file:</p><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;phpunit backupGlobals=&quot;false&quot;<br> backupStaticAttributes=&quot;false&quot;<br> bootstrap=&quot;bootstrap/autoload.php&quot;<br> colors=&quot;true&quot;<br> convertErrorsToExceptions=&quot;true&quot;<br> convertNoticesToExceptions=&quot;true&quot;<br> convertWarningsToExceptions=&quot;true&quot;<br> processIsolation=&quot;false&quot;<br> stopOnFailure=&quot;false&quot;<br> syntaxCheck=&quot;false&quot;<br>&gt;<br>  &lt;testsuites&gt;<br>    &lt;testsuite name=&quot;Application Test Suite&quot;&gt;<br>      &lt;directory&gt;./app/tests&lt;/directory&gt;<br>    &lt;/testsuite&gt;<br>  &lt;/testsuites&gt;<br>&lt;/phpunit&gt;</pre><blockquote>This file should be saved as <strong>phpunit.xml</strong>.</blockquote><p>This will get the tests running, and serves as a template in which to start writing our tests. You can actually see this test case working, by running the following command:</p><pre>❯ phpunit<br> <br>PHPUnit 4.0.14 by Sebastian Bergmann.<br> <br>Configuration read from /path/to/phpunit.xml<br> <br>.<br> <br>Time: 76 ms, Memory: 8.75Mb<br> <br>OK (1 test, 0 assertions)</pre><blockquote>This test is functional because it only happens after a Laravel instance is spun up, and it doesn’t care about what state it leaves this application instance in.</blockquote><p>Since we haven’t implemented the body of the <strong>send()</strong> method, it’s difficult for us to know what the return value will be. What we can test for is what methods (on the mailer’s underlying mail transport/interface) are being called…</p><p>Imagine we’re using the underlying Laravel mail class to send the emails. We used it before we started optimising the controller layer:</p><pre>Mail::send(&quot;emails.post&quot;, Input::all(), function($email) {<br>  $email<br>    -&gt;to(&quot;<a href="mailto:cgpitt@gmail.com">cgpitt@gmail.com</a>&quot;, &quot;Chris&quot;)<br>    -&gt;subject(&quot;New post&quot;);<br>}); </pre><blockquote>You can find out more about the Mailer class at: <a href="http://laravel.com/docs/mail"><strong>http://laravel.com/docs/mail</strong></a>.</blockquote><p>We’d essentially like to use this logic inside the PostMailer class. We should also dependency-inject our Mail provider:</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>use Illuminate\Mail\Mailer;<br> <br>class PostMailer implements PostMailerInterface<br>{<br>  public function __construct(Mailer $mailer)<br>  {<br>    $this-&gt;mailer = $mailer;<br>  }<br>  <br>  public function send($to, $view, $data)<br>  {<br>    $this-&gt;mailer-&gt;send(<br>      $view, $data,<br>      function($email) use ($to) {<br>        $email-&gt;to($to);<br>      }<br>    );<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/Formativ/PostMailer.php</strong>.</blockquote><p>Now we call the <strong>send()</strong> method on an injected mailer instance, instead of directly on the facade. This is still a little tricky to test (thanks to the callback), but thankfully much easier than if it was still using the facade (and in the controller):</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>use Mockery;<br>use TestCase;<br> <br>class PostMailerTest<br>extends TestCase<br>{<br>  public function tearDown()<br>  {<br>    Mockery::close();<br>  }<br>  <br>  public function testSend()<br>  {<br>    $mailerMock = $this-&gt;getMailerMock();<br>  <br>    $mailerMock<br>      -&gt;shouldReceive(&quot;send&quot;)<br>      -&gt;atLeast()-&gt;once()<br>      -&gt;with(<br>        &quot;bar&quot;, [&quot;baz&quot;],<br>        $this-&gt;getSendCallbackMock()<br>      );<br>  <br>    $postMailer = new PostMailer($mailerMock);<br>    $postMailer-&gt;send(&quot;foo&quot;, &quot;bar&quot;, [&quot;baz&quot;]);<br>  }<br>  <br>  protected function getSendCallbackMock()<br>  {<br>    return Mockery::on(function($callback) {<br>      $emailMock = Mockery::mock(&quot;stdClass&quot;);<br>  <br>      $emailMock<br>        -&gt;shouldReceive(&quot;to&quot;)<br>        -&gt;atLeast()-&gt;once()<br>        -&gt;with(&quot;foo&quot;);<br>  <br>      $callback($emailMock);<br>  <br>      return true;<br>    });<br>  }<br>  <br>  protected function getMailerMock()<br>  {<br>    return Mockery::mock(&quot;Illuminate\Mail\Mailer&quot;);<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/tests/Formativ/PostMailerTest.php</strong>.</blockquote><p>Phew! Let’s break that up so it’s easier to digest…</p><pre>Mockery::mock(&quot;Illuminate\Mail\Mailer&quot;);</pre><p>Part of trying to test in the most isolated manner is substituting dependencies with things that don’t perform any significant function. We do this by creating a new mock instance, via the <strong>Mockery::mock()</strong> method.</p><p>We use this again with:</p><pre>$emailMock = Mockery::mock(&quot;stdClass&quot;);</pre><p>We can use <strong>stdClass</strong> the second time around because the provided class isn’t type-hinted. Our <strong>PostMailer</strong> class type-hints the <strong>Illuminate\Mail\Mailer</strong> class.</p><p>We then tell the test to expect that certain methods are called using certain arguments:</p><pre>$emailMock<br>  -&gt;shouldReceive(&quot;to&quot;)<br>  -&gt;atLeast()<br>  -&gt;once()<br>  -&gt;with(&quot;foo&quot;);</pre><p>This tells the mock to expect a call to an as-yet undefined <strong>to()</strong> method, and to expect that it will be passed <strong>“foo”</strong> as the first (and single) argument. If your production code expects a stubbed method to return a specific kind of data, you can add the <strong>andReturn()</strong> method.</p><p>We can provide expected callbacks, though it’s slightly trickier:</p><pre>Mockery::on(function($callback) {<br>  $emailMock = Mockery::mock(&quot;stdClass&quot;);<br>  <br>  $emailMock<br>    -&gt;shouldReceive(&quot;to&quot;)<br>    -&gt;atLeast()<br>    -&gt;once()<br>    -&gt;with(&quot;foo&quot;);<br>  <br>  $callback($emailMock);<br>  <br>  return true;<br>});</pre><p>We set up a mock, which expects calls to it’s own methods, and then the original callback is run with the provided mock. Don’t forget to add <strong>return true</strong> — that tells mockery that it’s ok to run the callback with the mock you’ve set up.</p><p>At the end of all of this; we’re just testing that methods were called in the correct way. The test doesn’t worry about making sure the Laravel <strong>Mailer</strong> class actually sends the mail correctly — that has it’s own tests.</p><p>The repository class is slightly simpler to test:</p><pre>&lt;?php<br> <br>namespace Formativ;<br> <br>use Mockery;<br>use TestCase;<br> <br>class PostRepositoryTest<br>extends TestCase<br>{<br>  public function tearDown()<br>  {<br>    Mockery::close();<br>  }<br>  <br>  public function testSend()<br>  {<br>    $requestMock = $this-&gt;getRequestMock();<br>  <br>    $requestMock<br>      -&gt;shouldReceive(&quot;get&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;title&quot;);<br>  <br>    $requestMock<br>      -&gt;shouldReceive(&quot;get&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;subtitle&quot;);<br>  <br>    $requestMock<br>      -&gt;shouldReceive(&quot;get&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;body&quot;);<br>  <br>    $requestMock<br>      -&gt;shouldReceive(&quot;get&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;author&quot;);<br>  <br>    $postRepository = new PostRepository($requestMock);<br>    $postRepository-&gt;insert();<br>  }<br>  <br>  protected function getRequestMock()<br>  {<br>    return Mockery::mock(&quot;Illuminate\Http\Request&quot;);<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/tests/Formativ/PostRepositoryTest.php</strong>.</blockquote><p>All we’re doing here is making sure the get method is called four times, on the request dependency. We could extend this to accommodate requests against the underlying database connector object, and the test code would be similar.</p><p>We can’t completely test the <strong>Str::slug()</strong> method because it’s not a facade but rather a static method on the Str class. Every facade allows you to mock methods (facades subclass the <strong>MockObject</strong> class), and you can even swap them out with your own mocks (using the <strong>Validator::swap($validatorMock)</strong> method).</p><blockquote>You can test static method calls using the AspectMock library, which you can learn more about at: <a href="https://github.com/Codeception/AspectMock"><strong>https://github.com/Codeception/AspectMock</strong></a>.</blockquote><blockquote>You can learn more about facades at: <a href="http://laravel.com/docs/facades"><strong>http://laravel.com/docs/facades</strong></a>.</blockquote><p>Finally, let’s test the controller:</p><pre>&lt;?php<br> <br>class PostControllerTest<br>extends TestCase<br>{<br>  public function tearDown()<br>  {<br>    Mockery::close();<br>  }<br> <br>  public function testConstructor()<br>  {<br>    $repositoryMock = $this-&gt;getRepositoryMock();<br>  <br>    $mailerMock = $this-&gt;getMailerMock();<br>  <br>    $dispatcherMock = $this-&gt;getDispatcherMock();<br>  <br>    $dispatcherMock<br>      -&gt;shouldReceive(&quot;listen&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(<br>        &quot;post.store&quot;,<br>        [$repositoryMock, &quot;insert&quot;]<br>      );<br>  <br>    $dispatcherMock<br>      -&gt;shouldReceive(&quot;listen&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(<br>        &quot;post.store&quot;,<br>        [$mailerMock, &quot;send&quot;]<br>      );<br>  <br>    $postController = new PostController(<br>      $repositoryMock,<br>      $this-&gt;getValidatorMock(),<br>      $mailerMock,<br>      $this-&gt;getResponseMock(),<br>      $dispatcherMock<br>    );<br>  }<br> <br>  public function testStore()<br>  {<br>    $validatorMock = $this-&gt;getValidatorMock();<br>     <br>    $validatorMock<br>      -&gt;shouldReceive(&quot;passes&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;store&quot;)<br>      -&gt;andReturn(true);<br>       <br>    $responseMock = $this-&gt;getResponseMock();<br>     <br>    $responseMock<br>      -&gt;shouldReceive(&quot;route&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;posts.index&quot;)<br>      -&gt;andReturn($responseMock);<br>      <br>    $responseMock<br>      -&gt;shouldReceive(&quot;with&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;success&quot;, true);<br>     <br>    $dispatcherMock = $this-&gt;getDispatcherMock();<br>    <br>    $dispatcherMock<br>      -&gt;shouldReceive(&quot;fire&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;post.store&quot;);<br>    <br>    $postController = new PostController(<br>      $this-&gt;getRepositoryMock(),<br>      $validatorMock,<br>      $this-&gt;getMailerMock(),<br>      $responseMock,<br>      $dispatcherMock<br>    );<br>   <br>    $postController-&gt;store();<br>  }<br>  <br>  public function testStoreFails()<br>  {<br>    $validatorMock = $this-&gt;getValidatorMock();<br>  <br>    $validatorMock<br>      -&gt;shouldReceive(&quot;passes&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;store&quot;)<br>      -&gt;andReturn(false);<br>  <br>    $validatorMock<br>      -&gt;shouldReceive(&quot;messages&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with(&quot;store&quot;)<br>      -&gt;andReturn([&quot;foo&quot;]);<br>  <br>    $responseMock = $this-&gt;getResponseMock();<br>  <br>    $responseMock<br>      -&gt;shouldReceive(&quot;back&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;andReturn($responseMock);<br>  <br>    $responseMock<br>      -&gt;shouldReceive(&quot;withErrors&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;with([&quot;foo&quot;])<br>      -&gt;andReturn($responseMock);<br>  <br>    $responseMock<br>      -&gt;shouldReceive(&quot;withInput&quot;)<br>      -&gt;atLeast()<br>      -&gt;once()<br>      -&gt;andReturn($responseMock);<br>  <br>    $postController = new PostController(<br>      $this-&gt;getRepositoryMock(),<br>      $validatorMock,<br>      $this-&gt;getMailerMock(),<br>      $responseMock,<br>      $this-&gt;getDispatcherMock()<br>    );<br>  <br>    $postController-&gt;store();<br>  }<br> <br>  protected function getRepositoryMock()<br>  {<br>    return Mockery::mock(&quot;Formativ\PostRepositoryInterface&quot;)<br>      -&gt;makePartial();<br>  }<br>  <br>  protected function getValidatorMock()<br>  {<br>    return Mockery::mock(&quot;Formativ\PostValidatorInterface&quot;)<br>      -&gt;makePartial();<br>  }<br>  <br>  protected function getMailerMock()<br>  {<br>    return Mockery::mock(&quot;Formativ\PostMailerInterface&quot;)<br>      -&gt;makePartial();<br>  }<br>  <br>  protected function getResponseMock()<br>  {<br>    return Mockery::mock(&quot;Illuminate\Support\Facades\Response&quot;)<br>      -&gt;makePartial();<br>  }<br>  <br>  protected function getDispatcherMock()<br>  {<br>    return Mockery::mock(&quot;Illuminate\Events\Dispatcher&quot;)<br>      -&gt;makePartial();<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/tests/controllers/PostControllerTest.php</strong>.</blockquote><p>Here we’re still testing method calls, but we also test multiple paths through the <strong>store()</strong> method.</p><blockquote>We haven’t used any assertions, even though they are very useful for unit and functional testing. Feel free to use them to check output values…</blockquote><blockquote>You can find out more about Mockery at: <a href="https://github.com/padraic/mockery"><strong>https://github.com/padraic/mockery</strong></a>.</blockquote><blockquote>You can find out more about PHPUnit at: <a href="http://phpunit.de/"><strong>http://phpunit.de</strong></a>.</blockquote><h3>The Rabbit Hole</h3><p>The process of test-writing can take as much time as you want to give it. It’s best just to decide exactly what you need to test, and step away after that.</p><p>We’ve just looked at a very narrow area of testing, in our applications. You’re likely to have a richer data layer, and need a ton of tests for that. You’re probably going to want to test the rendering of views.</p><p>Don’t think this is an exhaustive reference for how to test, or even that this is the only way to functionally test your controller code. It’s simply a method I’ve found works for the applications I write.</p><h3>Alternatives</h3><p>The closest alternative to testing with PHPUnit is probably PHPSpec (<a href="http://www.phpspec.net/"><strong>http://www.phpspec.net</strong></a>). It uses a similar dialect of assertions and mocking.</p><p>If you’re looking to test, in a broader sense, consider looking into Behat (<a href="http://behat.org/"><strong>http://behat.org</strong></a>). It uses a descriptive, text-based language to define what behaviour a service/library should have.</p><h3>Conclusion</h3><p>If you enjoyed this tutorial; it would be helpful if you could click the <strong>Recommend</strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/150/1*05D0E3dhjsTkABDRORHt0w.png" /></figure><p>This tutorial comes <a href="https://leanpub.com/laravel4cookbook"><strong>from a book I’m writing</strong></a><strong>. </strong>If you like it and want to support future tutorials; please consider buying it. Half of all sales go to Laravel.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=48414f4782d0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/laravel-4-controller-testing-48414f4782d0">Laravel 4 Controller Testing</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 4 File-Based CMS]]></title>
            <link>https://medium.com/laravel-4/laravel-4-file-based-cms-4bca98a74f4d?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/4bca98a74f4d</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Sun, 02 Feb 2014 16:58:41 GMT</pubDate>
            <atom:updated>2014-02-05T07:14:57.099Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A Comprehensive Tutorial</h4><p><a href="http://octobercms.com/"><strong>OctoberCMS</strong></a> is a Laravel-based, pre-built CMS which was recently announced. I have yet to see the code powering what looks like a beautiful and efficient CMS system. So I thought I would try to implement some of the concepts presented in the introductory video as they illustrate valuable tips for working with Laravel.</p><blockquote>While much effort has been spent in the pursuit of accuracy; there’s a good chance you could stumble across a curly quote in a code listing, or some other egregious errata. Please make a note and I will fix where needed.</blockquote><blockquote>I have also uploaded this code to Github. You need simply follow the configuration instructions in this tutorial, after downloading the source code, and the application should run fine.</blockquote><blockquote>This assumes, of course, that you know how to do that sort of thing. If not; this shouldn’t be the first place you learn about making PHP applications.</blockquote><blockquote><a href="https://github.com/formativ/tutorial-laravel-4-file-based-cms"><strong>https://github.com/formativ/tutorial-laravel-4-file-based-cms</strong></a></blockquote><blockquote>If you spot differences between this tutorial and that source code, please raise it here or as a GitHub issue. Your help is greatly appreciated.</blockquote><h3><strong>Installing Dependencies</strong></h3><p>We’re developing a Laravel 4 application which has lots of server-side aspects; but there’s also an interactive interface. There be scripts!</p><p>For this; we’re using Bootstrap and jQuery. Download Bootstrap at: <a href="http://getbootstrap.com/"><strong>http://getbootstrap.com/</strong></a><strong> </strong>and unpack it into your public folder. Where you put the individual files makes little difference, but I have put the scripts in <strong>public/js</strong>, the stylesheets in <strong>public/css </strong>and the fonts in <strong>public/fonts. </strong>Where you see those paths in my source code; you should substitute them with your own.</p><p>Next up, download jQuery at: <a href="http://jquery.com/download/"><strong>http://jquery.com/download/</strong></a><strong> </strong>and unpack it into your public folder.</p><p>On the server-side, we’re going to be using Flysystem for reading and writing files. Add it to the Composer dependencies:</p><pre>&quot;require&quot; : {<br>  &quot;laravel/framework&quot; : &quot;4.1.*&quot;,<br>  &quot;league/flysystem&quot;  : &quot;0.2.*&quot;<br>},</pre><blockquote><em>This was extracted from </em><strong><em>composer.json.</em></strong></blockquote><p>Follow that up with:</p><pre>composer update</pre><h3>Rendering Templates</h3><p>We’ve often used <strong>View::make()</strong> to render views. It’s great for when we have pre-defined view files and we want Laravel to manage how they are rendered and stored. In this tutorial, we’re going to be rendering templates from strings. We’ll need to encapsulate some of how Laravel rendered templates, but it’ll also give us a good base for extending upon the Blade template syntax.</p><p>Let’s get started by creating a service provider:</p><pre>php artisan workbench formativ/cms</pre><p>This will generate the usual scaffolding for a new package. We need to add it in a few places, to be able to use it in our application:</p><pre>&quot;providers&quot; =&gt; [<br>  &quot;Formativ\Cms\CmsServiceProvider&quot;,<br>  // …remaining service providers<br>],</pre><blockquote>This was extracted from <strong>app/config/app.php</strong>.</blockquote><pre>&quot;autoload&quot;: {<br>  &quot;classmap&quot;: [<br>    // …<br>  ],<br>  &quot;psr-0&quot;: {<br>    &quot;Formativ\\Cms&quot;: &quot;workbench/formativ/cms/src/&quot;<br>  }<br>}</pre><blockquote>This was extracted from <strong>composer.json</strong>.</blockquote><p>Then we need to rebuild the composer autoloader:</p><pre>composer dump-autoload</pre><p>All this gets us to a place where we can start to add classes for encapsulating and extending Blade rendering. Let’s create some wrapper classes, and register them in the service provider:</p><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>interface CompilerInterface<br>{<br>  public function compileString($template);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/CompilerInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms\Compiler;<br> <br>use Formativ\Cms\CompilerInterface;<br>use Illuminate\View\Compilers\BladeCompiler;<br> <br>class Blade<br>extends BladeCompiler<br>implements CompilerInterface<br>{<br> <br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/Compiler/Blade.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>interface EngineInterface<br>{<br>  public function render($template, $data);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/EngineInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms\Engine;<br> <br>use Formativ\Cms\CompilerInterface;<br>use Formativ\Cms\EngineInterface;<br> <br>class Blade<br>implements EngineInterface<br>{<br>  protected $compiler;<br>  <br>  public function __construct(CompilerInterface $compiler)<br>  {<br>    $this-&gt;compiler = $compiler;<br>  }<br>  <br>  public function render($template, $data)<br>  {<br>    $compiled = $this-&gt;compiler-&gt;compileString($template);<br>  <br>    ob_start();<br>    extract($data, EXTR_SKIP);<br>  <br>    try<br>    {<br>      eval(&quot;?&gt;&quot; . $compiled);<br>    }<br>    catch (Exception $e)<br>    {<br>      ob_end_clean();<br>      throw $e;<br>    }<br>  <br>    $result = ob_get_contents();<br>    ob_end_clean();<br>  <br>    return $result;<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/Engine/Blade.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>use Illuminate\Support\ServiceProvider;<br> <br>class CmsServiceProvider<br>extends ServiceProvider<br>{<br>  protected $defer = true;<br>  <br>  public function register()<br>  {<br>    $this-&gt;app-&gt;bind(<br>      &quot;Formativ\Cms\CompilerInterface&quot;,<br>      function() {<br>        return new Compiler\Blade(<br>          $this-&gt;app-&gt;make(&quot;files&quot;),<br>          $this-&gt;app-&gt;make(&quot;path.storage&quot;) . &quot;/views&quot;<br>        );<br>      }<br>    );<br>    <br>    $this-&gt;app-&gt;bind(<br>      &quot;Formativ\Cms\EngineInterface&quot;,<br>      &quot;Formativ\Cms\Engine\Blade&quot;<br>    );<br>  }<br>  <br>  public function provides()<br>  {<br>    return [<br>      &quot;Formativ\Cms\CompilerInterface&quot;,<br>      &quot;Formativ\Cms\EngineInterface&quot;<br>    ];<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/CmsServiceProvider.php</strong>.</blockquote><p>The <strong>Compiler\Blade</strong> class encapsulates the <strong>BladeCompiler</strong> class, allowing us to implement the <strong>CompilerInterface</strong> interface. This is a way of future-proofing our package so that the code which depends on methods Blade currently implements won’t fail if future versions of Blade were to remove that implementation.</p><blockquote>We can also add additional template tags in via this class, so it’s not too much code bloat.</blockquote><p>The <strong>Engine\Blade</strong> class contains the method which we will use to render template strings. It implements the <strong>EngineInterface</strong> interface for that same future-proofing.</p><p>We register all of these in the <strong>CmsServiceProvider</strong>. We can now inject these dependencies in our controller:</p><pre>&lt;?php<br> <br>use Formativ\Cms\EngineInterface;<br> <br>class IndexController<br>extends BaseController<br>{<br>  protected $engine;<br>  <br>  public function __construct(EngineInterface $engine)<br>  {<br>    $this-&gt;engine = $engine;<br>  }<br>  <br>  public function indexAction()<br>  {<br>    // ...use $this-&gt;engine-&gt;render() here<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><p>As we bound <strong>Formativ\Cms\EngineInterface</strong> in our service provider, we can now specify it in our controller constructor and Laravel will automatically inject it for us.</p><h3>Gathering Metadata</h3><p>One of the interesting things OctoberCMS does is store all of the page and layout meta data at the top of the template file. This allows changes to metadata (which would normally take place elsewhere) to be version-controlled. Having gained the ability to render template strings, we’re now in a position to be able to isolate this kind of metadata and render the rest of the file as a template.</p><p>Consider the following example:</p><pre>protected function minify($html)<br>{<br>  $search = [<br>    &quot;/\&gt;[^\S ]+/s&quot;,<br>    &quot;/[^\S ]+\&lt;/s&quot;,<br>    &quot;/(\s)+/s&quot;<br>  ];<br>  <br>  $replace = [<br>    &quot;&gt;&quot;,<br>    &quot;&lt;&quot;,<br>    &quot;\\1&quot;<br>  ];<br>  <br>  $html = preg_replace($search, $replace, $html);<br>  <br>  return $html;<br>}<br>  <br>public function indexAction()<br>{<br>  $template = $this-&gt;minify(&quot;<br>    &lt;!doctype html&gt;<br>    &lt;html lang=&#39;en&#39;&gt;<br>      &lt;head&gt;<br>        &lt;title&gt;<br>          Laravel 4 File-Based CMS<br>        &lt;/title&gt;<br>      &lt;/head&gt;<br>      &lt;body&gt;<br>        Hello world<br>      &lt;/body&gt;<br>    &lt;/html&gt;<br>  &quot;);<br>  <br>  return $this-&gt;engine-&gt;render($template, []);<br>}</pre><blockquote>This was extracted from <strong>app/controllers/IndexController.php</strong>.</blockquote><p>Here we’re rendering a page template (with the help of a minify method). It’s just like what we did before. Let’s add some metadata, and pull it out of the template before rendering:</p><pre>protected function extractMeta($html)<br>{<br>  $parts = explode(&quot;==&quot;, $html, 2);<br>  <br>  $meta = &quot;&quot;;<br>  $html = $parts[0];<br>  <br>  if (count($parts) &gt; 1)<br>  {<br>    $meta = $parts[0];<br>    $html = $parts[1];<br>  }<br>  <br>  return [<br>    &quot;meta&quot; =&gt; $meta,<br>    &quot;html&quot; =&gt; $html<br>  ];<br>}<br> <br>protected function parseMeta($meta)<br>{<br>  $meta  = trim($meta);<br>  $lines = explode(&quot;\n&quot;, $meta);<br>  $data  = [];<br>  <br>  foreach ($lines as $line)<br>  {<br>    $parts = explode(&quot;=&quot;, $line);<br>    $data[trim($parts[0])] = trim($parts[1]);<br>  }<br>  <br>  return $data;<br>}<br> <br>public function indexAction()<br>{<br>  $parts = $this-&gt;extractMeta(&quot;<br>    title   = Laravel 4 File-Based CMS<br>    message = Hello world<br>    ==<br>    &lt;!doctype html&gt;<br>    &lt;html lang=&#39;en&#39;&gt;<br>      &lt;head&gt;<br>        &lt;title&gt;<br>          {{ \$title }}<br>        &lt;/title&gt;<br>      &lt;/head&gt;<br>      &lt;body&gt;<br>        {{ \$message }}<br>      &lt;/body&gt;<br>    &lt;/html&gt;<br>  &quot;);<br> <br>  $data     = $this-&gt;parseMeta($parts[&quot;meta&quot;]);<br>  $template = $this-&gt;minify($parts[&quot;html&quot;]);<br> <br>  return $this-&gt;engine-&gt;render($template, $data);<br>}</pre><blockquote>This was extracted from <strong>app/controllers/IndexController.php</strong>.</blockquote><p>This time round, we’re using an <strong>extractMeta()</strong> method to pull the meta data string out of the template string, and a <strong>parseMeta()</strong> method to split the lines of metadata into key/value pairs.</p><p>The result is a functional means of storing and parsing meta data, and rendering the remaining template from and to a string.</p><blockquote>The minify method is largely unmodified from the original, which I found at: <a href="http://stackoverflow.com/questions/6225351/how-to-minify-php-page-html-output"><strong>http://stackoverflow.com/questions/6225351/how-to-minify-php-page-html-output</strong></a>.</blockquote><h3>Creating Layouts</h3><p>We need to create some sort of admin interface, with which to create and/or modify pages and layouts. Let’s skip the authentication system (as we’ve done that before and it will distract from the focus of this tutorial).</p><p>I’ve chosen for us to use Flysystem when working with the filesystem. It would be a good idea to future-proof this dependency by wrapping it in a subclass which implements an interface we control.</p><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>interface FilesystemInterface<br>{<br>  public function has($file);<br>  public function listContents($folder, $detail = false);<br>  public function write($file, $contents);<br>  public function read($file);<br>  public function put($file, $content);<br>  public function delete($file);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/FilesystemInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>use League\Flysystem\Filesystem as Base;<br> <br>class Filesystem<br>extends Base<br>implements FilesystemInterface<br>{<br> <br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/Filesystem.php</strong>.</blockquote><pre>public function register()<br>{<br>  $this-&gt;app-&gt;bind(<br>    &quot;Formativ\Cms\CompilerInterface&quot;,<br>    function() {<br>      return new Compiler\Blade(<br>        $this-&gt;app-&gt;make(&quot;files&quot;),<br>        $this-&gt;app-&gt;make(&quot;path.storage&quot;) . &quot;/views&quot;<br>      );<br>    }<br>  );<br>  <br>  $this-&gt;app-&gt;bind(<br>    &quot;Formativ\Cms\EngineInterface&quot;,<br>    &quot;Formativ\Cms\Engine\Blade&quot;<br>  );<br>  <br>  $this-&gt;app-&gt;bind(<br>    &quot;Formativ\Cms\FilesystemInterface&quot;,<br>    function() {<br>      return new Filesystem(<br>        new Local(<br>          $this-&gt;app-&gt;make(&quot;path.base&quot;) . &quot;/app/views&quot;<br>        )<br>      );<br>    }<br>  );<br>}</pre><blockquote>This was extracted from <strong>workbench/formativ/cms/src/Formativ/<br>Cms/CmsServiceProvider.php</strong>.</blockquote><p>We’re not really adding any extra functionality to that which Flysystem provides. The sole purpose of us wrapping the <strong>Local</strong> Flysystem adapter is to make provision for swapping it with another filesystem class/library.</p><p>We should also move the metadata-related functionality into a better location.</p><pre>&lt;?php<br> <br>namespace Formativ\Cms;<br> <br>interface EngineInterface<br>{<br>  public function render($template, $data);<br>  public function extractMeta($template);<br>  public function parseMeta($meta);<br>  public function minify($template);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/EngineInterface.php</strong>.</blockquote><pre>&lt;?php<br> <br>namespace Formativ\Cms\Engine;<br> <br>use Formativ\Cms\CompilerInterface;<br>use Formativ\Cms\EngineInterface;<br> <br>class Blade<br>implements EngineInterface<br>{<br>  protected $compiler;<br>  <br>  public function __construct(CompilerInterface $compiler)<br>  {<br>    $this-&gt;compiler = $compiler;<br>  }<br>  <br>  public function render($template, $data)<br>  {<br>    $extracted = $this-&gt;extractMeta($template)[&quot;template&quot;];<br>    $compiled = $this-&gt;compiler-&gt;compileString($extracted);<br>  <br>    ob_start();<br>    extract($data, EXTR_SKIP);<br>  <br>    try<br>    {<br>      eval(&quot;?&gt;&quot; . $compiled);<br>    }<br>    catch (Exception $e)<br>    {<br>      ob_end_clean();<br>      throw $e;<br>    }<br>  <br>    $result = ob_get_contents();<br>    ob_end_clean();<br>  <br>    return $result;<br>  }<br>  <br>  public function minify($template)<br>  {<br>    $search = [<br>      &quot;/\&gt;[^\S ]+/s&quot;, <br>      &quot;/[^\S ]+\&lt;/s&quot;,<br>      &quot;/(\s)+/s&quot;<br>    ];<br>  <br>    $replace = [<br>      &quot;&gt;&quot;,<br>      &quot;&lt;&quot;,<br>      &quot;\\1&quot;<br>    ];<br>  <br>    $template = preg_replace($search, $replace, $template);<br>  <br>    return $template;<br>  }<br>  <br>  public function extractMeta($template)<br>  {<br>    $parts = explode(&quot;==&quot;, $template, 2);<br>  <br>    $meta     = &quot;&quot;;<br>    $template = $parts[0];<br>  <br>    if (count($parts) &gt; 1)<br>    {<br>      $meta     = $parts[0];<br>      $template = $parts[1];<br>    }<br>  <br>    return [<br>      &quot;meta&quot;     =&gt; $meta,<br>      &quot;template&quot; =&gt; $template<br>    ];<br>  }<br>  <br>  public function parseMeta($meta)<br>  {<br>    $meta  = trim($meta);<br>    $lines = explode(&quot;\n&quot;, $meta);<br>    $data  = [];<br>  <br>    foreach ($lines as $line)<br>    {<br>      $parts = explode(&quot;=&quot;, $line);<br>      $data[trim($parts[0])] = trim($parts[1]);<br>    }<br>  <br>    return $data;<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/cms/src/<br>Formativ/Cms/Engine/Blade.php</strong>.</blockquote><p>The only difference can be found in the argument names (to bring them more in line with the rest of the class) and integrating the meta methods into the <strong>render()</strong> method.</p><p>Next up is layout controller class:</p><pre>&lt;?php<br> <br>use Formativ\Cms\EngineInterface;<br>use Formativ\Cms\FilesystemInterface;<br> <br>class LayoutController<br>extends BaseController<br>{<br>  protected $engine;<br>  protected $filesystem;<br>  <br>  public function __construct(<br>    EngineInterface $engine,<br>    FilesystemInterface $filesystem<br>  )<br>  {<br>    $this-&gt;engine     = $engine;<br>    $this-&gt;filesystem = $filesystem;<br>  <br>    Validator::extend(<br>      &quot;add&quot;,<br>      function($attribute, $value, $params) {<br>        return !$this-&gt;filesystem-&gt;has(&quot;layouts/&quot; . $value);<br>      }<br>    );<br> <br>    Validator::extend(<br>      &quot;edit&quot;,<br>      function($attribute, $value, $params) {<br>        $new  = !$this-&gt;filesystem-&gt;has(&quot;layouts/&quot; . $value);<br>        $same = $this-&gt;filesystem-&gt;has(&quot;layouts/&quot; . $params[0]);<br>  <br>        return $new or $same;<br>      }<br>    );<br>  }<br> <br>  public function indexAction()<br>  {<br>    $layouts = $this-&gt;filesystem-&gt;listContents(&quot;layouts&quot;);<br>    $edit    = URL::route(&quot;admin/layout/edit&quot;) . &quot;?layout=&quot;;<br>    $delete  = URL::route(&quot;admin/layout/delete&quot;) . &quot;?layout=&quot;;<br>  <br>    return View::make(&quot;admin/layout/index&quot;, compact(<br>      &quot;layouts&quot;,<br>      &quot;edit&quot;,<br>      &quot;delete&quot;<br>    ));<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/LayoutController.php</strong>.</blockquote><p>This is the first time we’re using Dependency Injection in our controller. Laravel is injecting our engine interface (which is the Blade wrapper) and our filesystem interface (which is the Flysystem wrapper). As usual, we assign the injected dependencies to protected properties. We also define two custom validation rules, which we’ll use when adding and editing the layout files.</p><p>We’ve also defined an <strong>indexAction()</strong> method which will be used to display a list of layout files which can then be edited or deleted. For the interface to be complete, we are going to need the following files:</p><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot; /&gt;<br>    &lt;title&gt;Laravel 4 File-Based CMS&lt;/title&gt;<br>    &lt;link<br>      rel=&quot;stylesheet&quot;<br>      href=&quot;{{ asset(&quot;css/bootstrap.min.css&quot;); }}&quot;<br>    /&gt;<br>    &lt;link<br>      rel=&quot;stylesheet&quot;<br>      href=&quot;{{ asset(&quot;css/shared.css&quot;); }}&quot;<br>    /&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    @include(&quot;admin/include/navigation&quot;)<br>    &lt;div class=&quot;container&quot;&gt;<br>      &lt;div class=&quot;row&quot;&gt;<br>        &lt;div class=&quot;column md-12&quot;&gt;<br>          @yield(&quot;content&quot;)<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>    &lt;script src=&quot;{{ asset(&quot;js/jquery.min.js&quot;); }}&quot;&gt;&lt;/script&gt;<br>    &lt;script src=&quot;{{ asset(&quot;js/bootstrap.min.js&quot;); }}&quot;&gt;&lt;/script&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/layout.blade.php</strong>.</blockquote><pre>&lt;nav<br> class=&quot;navbar navbar-inverse navbar-fixed-top&quot;<br> role=&quot;navigation&quot;<br>&gt;<br>  &lt;div class=&quot;container-fluid&quot;&gt;<br>    &lt;div class=&quot;navbar-header&quot;&gt;<br>      &lt;button type=&quot;button&quot;<br>        class=&quot;navbar-toggle&quot;<br>        data-toggle=&quot;collapse&quot;<br>        data-target=&quot;#navbar-collapse&quot;<br>      &gt;<br>      &lt;span class=&quot;sr-only&quot;&gt;Toggle navigation&lt;/span&gt;<br>      &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>      &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>      &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>    &lt;/button&gt;<br>  &lt;/div&gt;<br>  &lt;div<br>    class=&quot;collapse navbar-collapse&quot;<br>    id=&quot;navbar-collapse&quot;<br>  &gt;<br>    &lt;ul class=&quot;nav navbar-nav&quot;&gt;<br>      &lt;li class=&quot;@yield(&quot;navigation/layout/class&quot;)&quot;&gt;<br>        &lt;a href=&quot;{{ URL::route(&quot;admin/layout/index&quot;) }}&quot;&gt;<br>          Layouts<br>        &lt;/a&gt;<br>      &lt;/li&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/nav&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/include/navigation.blade.php</strong>.</blockquote><pre>&lt;ol class=&quot;breadcrumb&quot;&gt;<br>  &lt;li&gt;<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/layout/index&quot;) }}&quot;&gt;<br>      List Layouts<br>    &lt;/a&gt;<br>  &lt;/li&gt;<br>  &lt;li&gt;<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/layout/add&quot;) }}&quot;&gt;<br>      Add New Layout<br>    &lt;/a&gt;<br>  &lt;/li&gt;<br>&lt;/ol&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/include/<br>layout/navigation.blade.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/layout/class&quot;)<br>  active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/layout/navigation&quot;)<br>  @if (count($layouts))<br>    &lt;table class=&quot;table table-striped&quot;&gt;<br>      &lt;thead&gt;<br>        &lt;tr&gt;<br>          &lt;th class=&quot;wide&quot;&gt;<br>            File<br>          &lt;/th&gt;<br>          &lt;th class=&quot;narrow&quot;&gt;<br>            Actions<br>          &lt;/th&gt;<br>        &lt;/tr&gt;<br>      &lt;/thead&gt;<br>      &lt;tbody&gt;<br>        @foreach ($layouts as $layout)<br>          @if ($layout[&quot;type&quot;] == &quot;file&quot;)<br>            &lt;tr&gt;<br>              &lt;td class=&quot;wide&quot;&gt;<br>                &lt;a href=&quot;{{ $edit . $layout[&quot;basename&quot;] }}&quot;&gt;<br>                  {{ $layout[&quot;basename&quot;] }}<br>                &lt;/a&gt;<br>              &lt;/td&gt;<br>              &lt;td class=&quot;narrow actions&quot;&gt;<br>                &lt;a href=&quot;{{ $edit . $layout[&quot;basename&quot;] }}&quot;&gt;<br>                  &lt;i class=&quot;glyphicon glyphicon-pencil&quot;&gt;&lt;/i&gt;<br>                &lt;/a&gt;<br>                &lt;a href=&quot;{{ $delete . $layout[&quot;basename&quot;] }}&quot;&gt;<br>                  &lt;i class=&quot;glyphicon glyphicon-trash&quot;&gt;&lt;/i&gt;<br>                &lt;/a&gt;<br>              &lt;/td&gt;<br>            &lt;/tr&gt;<br>          @endif<br>        @endforeach<br>      &lt;/tbody&gt;<br>    &lt;/table&gt;<br>  @else<br>    No layouts yet.<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/layout/add&quot;) }}&quot;&gt;<br>      create one now!<br>    &lt;/a&gt;<br>  @endif<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/layout/index.blade.php</strong>.</blockquote><pre>Route::any(&quot;admin/layout/index&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/layout/index&quot;,<br>  &quot;uses&quot; =&gt; &quot;LayoutController@indexAction&quot;<br>]);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>These are quite a few files, so let’s go over them individually:</p><ol><li>The first file is the main admin layout template. Every page in the admin area should be rendered within this layout template. As you can see, we’ve linked the jQuery and Bootstrap assets to provide some styling and interactive functionality to the admin area.</li><li>The second file is the main admin navigation template. This will also be present on every page in the admin area, though it’s better to include it in the main layout template than to clutter the main layout template with secondary markup.</li><li>The third file is a sub-navigation template, only present in the pages concerning layouts.</li><li>The fourth file is the layout index (or listing) page. It includes the sub-navigation and renders a table row for each layout file it finds. If none can be found, it will present a cheeky message for the user to add one.</li><li>Finally we add the index route to the routes.php file. At this point, the page should be visible via the browser. As there aren’t any layout files yet, you should see the cheeky message instead.</li></ol><p>Let’s move onto the layout add page:</p><pre>public function addAction()<br>{<br>  if (Input::has(&quot;save&quot;))<br>  {<br>    $validator = Validator::make(Input::all(), [<br>      &quot;name&quot; =&gt; &quot;required|add&quot;,<br>      &quot;code&quot; =&gt; &quot;required&quot;<br>    ]);<br>  <br>    if ($validator-&gt;fails())<br>    {<br>      return Redirect::route(&quot;admin/layout/add&quot;)<br>        -&gt;withInput()<br>        -&gt;withErrors($validator);<br>    }<br>  <br>    $meta = &quot;<br>      title       = &quot; . Input::get(&quot;title&quot;) . &quot;<br>      description = &quot; . Input::get(&quot;description&quot;) . &quot;<br>      ==<br>    &quot;;<br>  <br>    $name = &quot;layouts/&quot; . Input::get(&quot;name&quot;) . &quot;.blade.php&quot;;<br>  <br>    $this-&gt;filesystem-&gt;write($name, $meta . Input::get(&quot;code&quot;));<br>  <br>    return Redirect::route(&quot;admin/layout/index&quot;);<br>  }<br>  <br>  return View::make(&quot;admin/layout/add&quot;);<br>}</pre><blockquote>This was extracted from <strong>app/controllers/LayoutController.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/layout/class&quot;)<br>  active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/layout/navigation&quot;)<br>  &lt;form role=&quot;form&quot; method=&quot;post&quot;&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;name&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;name&quot;<br>        name=&quot;name&quot;<br>        placeholder=&quot;new-layout&quot;<br>        value=&quot;{{ Input::old(&quot;name&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;title&quot;&gt;Meta Title&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;title&quot;<br>        name=&quot;title&quot;<br>        value=&quot;{{ Input::old(&quot;title&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;description&quot;&gt;Meta Description&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;  <br>        class=&quot;form-control&quot;<br>        id=&quot;description&quot;<br>        name=&quot;description&quot;<br>        value=&quot;{{ Input::old(&quot;description&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;code&quot;&gt;Code&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;code&quot;) }}<br>      &lt;/span&gt;<br>      &lt;textarea<br>        class=&quot;form-control&quot;<br>        id=&quot;code&quot;<br>        name=&quot;code&quot;<br>        rows=&quot;5&quot;<br>        placeholder=&quot;&amp;lt;div&amp;gt;Hello world&amp;lt;/div&amp;gt;&quot;<br>      &gt;{{ Input::old(&quot;code&quot;) }}&lt;/textarea&gt;<br>    &lt;/div&gt;<br>    &lt;input<br>      type=&quot;submit&quot;<br>      name=&quot;save&quot;<br>      class=&quot;btn btn-default&quot;<br>      value=&quot;Save&quot;<br>    /&gt;<br>  &lt;/form&gt;<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/layout/add.blade.php</strong>.</blockquote><pre>Route::any(&quot;admin/layout/add&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/layout/add&quot;,<br>  &quot;uses&quot; =&gt; &quot;LayoutController@addAction&quot;<br>]);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>The form processing, in the <strong>addAction()</strong> method, is wrapped in a check for the <strong>save</strong> parameter. This is the name of the submit button on the add form. We specify the validation rules (including one of those we defined in the constructor). If validation fails, we redirect back to the add page, bringing along the errors and old input. If not, we create a new file with the default meta title and default meta description as metadata. Finally we redirect to the index page.</p><p>The view is fairly standard (including the bootstrap tags we’ve used). The name and code fields have error messages and all of the fields have their values set to the old input values. We’ve also added a route to the add page.</p><p>Edit follows a similar pattern:</p><pre>public function editAction()<br>{<br>  $layout      = Input::get(&quot;layout&quot;);<br>  $name        = str_ireplace(&quot;.blade.php&quot;, &quot;&quot;, $layout);<br>  $content     = $this-&gt;filesystem-&gt;read(&quot;layouts/&quot; . $layout);<br>  $extracted   = $this-&gt;engine-&gt;extractMeta($content);<br>  $code        = trim($extracted[&quot;template&quot;]);<br>  $parsed      = $this-&gt;engine-&gt;parseMeta($extracted[&quot;meta&quot;]);<br>  $title       = $parsed[&quot;title&quot;];<br>  $description = $parsed[&quot;description&quot;];<br>  <br>  if (Input::has(&quot;save&quot;))<br>  {<br>    $validator = Validator::make(Input::all(), [<br>      &quot;name&quot; =&gt; &quot;required|edit:&quot; . Input::get(&quot;layout&quot;),<br>      &quot;code&quot; =&gt; &quot;required&quot;<br>    ]);<br>  <br>    if ($validator-&gt;fails())<br>    {<br>      return Redirect::route(&quot;admin/layout/edit&quot;)<br>        -&gt;withInput()<br>        -&gt;withErrors($validator);<br>    }<br>  <br>    $meta = &quot;<br>      title       = &quot; . Input::get(&quot;title&quot;) . &quot;<br>      description = &quot; . Input::get(&quot;description&quot;) . &quot;<br>      ==<br>    &quot;;<br>  <br>    $name = &quot;layouts/&quot; . Input::get(&quot;name&quot;) . &quot;.blade.php&quot;;<br>  <br>    $this-&gt;filesystem-&gt;put($name, $meta . Input::get(&quot;code&quot;));<br>  <br>    return Redirect::route(&quot;admin/layout/index&quot;);<br>  }<br>  <br>  return View::make(&quot;admin/layout/edit&quot;, compact(<br>    &quot;name&quot;,<br>    &quot;title&quot;,<br>    &quot;description&quot;,<br>    &quot;code&quot;<br>  ));<br>}</pre><blockquote>This was extracted from <strong>app/controllers/LayoutController.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/layout/class&quot;)<br>  active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/layout/navigation&quot;)<br>  &lt;form role=&quot;form&quot; method=&quot;post&quot;&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;name&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;name&quot;<br>        name=&quot;name&quot;<br>        placeholder=&quot;new-layout&quot;<br>        value=&quot;{{ Input::old(&quot;name&quot;, $name) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;title&quot;&gt;Meta Title&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;title&quot;<br>        name=&quot;title&quot;<br>        value=&quot;{{ Input::old(&quot;title&quot;, $title) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;description&quot;&gt;Meta Description&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;description&quot;<br>        name=&quot;description&quot;<br>        value=&quot;{{ Input::old(&quot;description&quot;, $description) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;code&quot;&gt;Code&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;code&quot;) }}<br>      &lt;/span&gt;<br>      &lt;textarea<br>        class=&quot;form-control&quot;<br>        id=&quot;code&quot;<br>        name=&quot;code&quot;<br>        rows=&quot;5&quot;<br>        placeholder=&quot;&amp;lt;div&amp;gt;Hello world&amp;lt;/div&amp;gt;&quot;<br>      &gt;{{ Input::old(&quot;code&quot;, $code) }}&lt;/textarea&gt;<br>    &lt;/div&gt;<br>    &lt;input<br>      type=&quot;submit&quot;<br>      name=&quot;save&quot;<br>      class=&quot;btn btn-default&quot;<br>      value=&quot;Save&quot;<br>    /&gt;<br>  &lt;/form&gt;<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/layout/edit.blade.php</strong>.</blockquote><pre>Route::any(&quot;admin/layout/edit&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/layout/edit&quot;,<br>  &quot;uses&quot; =&gt; &quot;LayoutController@editAction&quot;<br>]);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>The <strong>editAction()</strong> method fetches the layout file data and extracts/parses the metadata, so that we can present it in the edit form. Other than utilising the second custom validation function, we define in the constructor, there’s nothing else noteworthy in this method.</p><p>The edit form is also pretty much the same, except that we provide default values to the <strong>Input::old()</strong> method calls, giving the data extracted from the layout file. We also add a route to the edit page.</p><blockquote>You may notice that the file name remains editable, on the edit page. When you change this value, and save the layout it won’t change the name of the current layout file, but rather generate a new layout file. This is an interesting (and in this case useful) side-effect of using the file name as the unique identifier for layout files.</blockquote><p>Deleting layout files is even simpler:</p><pre>public function deleteAction()<br>{<br>  $name = &quot;layouts/&quot; . Input::get(&quot;layout&quot;);<br>  $this-&gt;filesystem-&gt;delete($name);<br>  <br>  return Redirect::route(&quot;admin/layout/index&quot;);<br>}</pre><blockquote>This was extracted from <strong>app/controllers/LayoutController.php</strong>.</blockquote><pre>Route::any(&quot;admin/layout/delete&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/layout/delete&quot;,<br>  &quot;uses&quot; =&gt; &quot;LayoutController@deleteAction&quot;<br>]);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>We link straight to the <strong>deleteAction()</strong> method in the index view. This method simply deletes the layout file and redirects back to the index page. We’ve added the appropriate route to make this page accessible.</p><p>We can now list the layout files, add new ones, edit existing ones and delete those layout files we no longer require. It’s basic, and could definitely be polished a bit, but it’s sufficient for our needs.</p><h3>Creating Pages</h3><p>Pages are handled in much the same way, so we’re not going to spend too much time on them. Let’s begin with the controller:</p><pre>&lt;?php<br> <br>use Formativ\Cms\EngineInterface;<br>use Formativ\Cms\FilesystemInterface;<br> <br>class PageController<br>extends BaseController<br>{<br>  protected $engine;<br>  protected $filesystem;<br>  <br>  public function __construct(<br>    EngineInterface $engine,<br>    FilesystemInterface $filesystem<br>  )<br>  {<br>    $this-&gt;engine = $engine;<br>    $this-&gt;filesystem = $filesystem;<br>  <br>    Validator::extend(<br>      &quot;add&quot;,<br>      function($attribute, $value, $parameters) {<br>        return !$this-&gt;filesystem-&gt;has(&quot;pages/&quot; . $value);<br>      }<br>    );<br>  <br>    Validator::extend(<br>      &quot;edit&quot;,<br>      function($attribute, $value, $params) {<br>        $new = !$this-&gt;filesystem-&gt;has(&quot;pages/&quot; . $value);<br>        $same = $this-&gt;filesystem-&gt;has(&quot;pages/&quot; . $params[0]);<br>  <br>        return $new or $same;<br>      }<br>    );<br>  }<br>  <br>  public function indexAction()<br>  {<br>    $pages  = $this-&gt;filesystem-&gt;listContents(&quot;pages&quot;);<br>    $edit   = URL::route(&quot;admin/page/edit&quot;) . &quot;?page=&quot;;<br>    $delete = URL::route(&quot;admin/page/delete&quot;) . &quot;?page=&quot;;<br>  <br>    return View::make(&quot;admin/page/index&quot;, compact(<br>      &quot;pages&quot;,<br>      &quot;edit&quot;,<br>      &quot;delete&quot;<br>    ));<br>  }<br>  <br>  public function addAction()<br>  {<br>    $files   = $this-&gt;filesystem-&gt;listContents(&quot;layouts&quot;);<br>    $layouts = [];<br>  <br>    foreach ($files as $file)<br>    {<br>      $name = $file[&quot;basename&quot;];<br>      $layouts[$name] = $name;<br>    }<br>  <br>    if (Input::has(&quot;save&quot;))<br>    {<br>      $validator = Validator::make(Input::all(), [<br>        &quot;name&quot;   =&gt; &quot;required|add&quot;,<br>        &quot;route&quot;  =&gt; &quot;required&quot;,<br>        &quot;layout&quot; =&gt; &quot;required&quot;,<br>        &quot;code&quot;   =&gt; &quot;required&quot;<br>      ]);<br>  <br>      if ($validator-&gt;fails())<br>      {<br>        return Redirect::route(&quot;admin/page/add&quot;)<br>          -&gt;withInput()<br>          -&gt;withErrors($validator);<br>      }<br>  <br>      $meta = &quot;<br>        title       = &quot; . Input::get(&quot;title&quot;) . &quot;<br>        description = &quot; . Input::get(&quot;description&quot;) . &quot;<br>        layout      = &quot; . Input::get(&quot;layout&quot;) . &quot;<br>        route       = &quot; . Input::get(&quot;route&quot;) . &quot;<br>        ==<br>      &quot;;<br>  <br>      $name = &quot;pages/&quot; . Input::get(&quot;name&quot;) . &quot;.blade.php&quot;;<br>      $code = $meta . Input::get(&quot;code&quot;);<br> <br>      $this-&gt;filesystem-&gt;write($name, $code);<br>  <br>      return Redirect::route(&quot;admin/page/index&quot;);<br>    }<br>  <br>    return View::make(&quot;admin/page/add&quot;, compact(<br>      &quot;layouts&quot;<br>    ));<br>  }<br>  <br>  public function editAction()<br>  {<br>    $files = $this-&gt;filesystem-&gt;listContents(&quot;layouts&quot;);<br>    $layouts = [];<br>  <br>    foreach ($files as $file)<br>    {<br>      $name = $file[&quot;basename&quot;];<br>      $layouts[$name] = $name;<br>    }<br>   <br>    $page        = Input::get(&quot;page&quot;);<br>    $name        = str_ireplace(&quot;.blade.php&quot;, &quot;&quot;, $page);<br>    $content     = $this-&gt;filesystem-&gt;read(&quot;pages/&quot; . $page);<br>    $extracted   = $this-&gt;engine-&gt;extractMeta($content);<br>    $code        = trim($extracted[&quot;template&quot;]);<br>    $parsed      = $this-&gt;engine-&gt;parseMeta($extracted[&quot;meta&quot;]);<br>    $title       = $parsed[&quot;title&quot;];<br>    $description = $parsed[&quot;description&quot;];<br>    $route       = $parsed[&quot;route&quot;];<br>    $layout      = $parsed[&quot;layout&quot;];<br>  <br>    if (Input::has(&quot;save&quot;))<br>    {<br>      $validator = Validator::make(Input::all(), [<br>        &quot;name&quot;   =&gt; &quot;required|edit:&quot; . Input::get(&quot;page&quot;),<br>        &quot;route&quot;  =&gt; &quot;required&quot;,<br>        &quot;layout&quot; =&gt; &quot;required&quot;,<br>        &quot;code&quot;   =&gt; &quot;required&quot;<br>      ]);<br>  <br>      if ($validator-&gt;fails())<br>      {<br>        return Redirect::route(&quot;admin/page/edit&quot;)<br>          -&gt;withInput()<br>          -&gt;withErrors($validator);<br>      }<br>  <br>      $meta = &quot;<br>        title       = &quot; . Input::get(&quot;title&quot;) . &quot;<br>        description = &quot; . Input::get(&quot;description&quot;) . &quot;<br>        layout      = &quot; . Input::get(&quot;layout&quot;) . &quot;<br>        route       = &quot; . Input::get(&quot;route&quot;) . &quot;<br>        ==<br>      &quot;;<br>  <br>      $name = &quot;pages/&quot; . Input::get(&quot;name&quot;) . &quot;.blade.php&quot;;<br>      $code = $meta . Input::get(&quot;code&quot;);<br> <br>      $this-&gt;filesystem-&gt;put($name, $code);<br>  <br>      return Redirect::route(&quot;admin/page/index&quot;);<br>    }<br>  <br>    return View::make(&quot;admin/page/edit&quot;, compact(<br>      &quot;name&quot;,<br>      &quot;title&quot;,<br>      &quot;description&quot;,<br>      &quot;layout&quot;,<br>      &quot;layouts&quot;,<br>      &quot;route&quot;,<br>      &quot;code&quot;<br>    ));<br>  }<br>  <br>  public function deleteAction()<br>  {<br>    $name = &quot;pages/&quot; . Input::get(&quot;page&quot;);<br>    $this-&gt;filesystem-&gt;delete($name);<br>  <br>    return Redirect::route(&quot;admin/page/index&quot;);<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/PageController.php</strong>.</blockquote><p>The constructor method accepts the same injected dependencies as our layout controller did. We also define similar custom validation rules to check the names of files we want to save.</p><p>The <strong>addAction()</strong> method differs slightly in that we load the existing layout files so that we can designate the layout for each page. We also add this (and the route parameter) to the metadata saved to the page file.</p><p>The <strong>editAction()</strong> method loads the route and layout parameters (in addition to the other fields) and passes them to the edit page template, where they will be used to populate the new fields.</p><pre>&lt;nav<br>  class=&quot;navbar navbar-inverse navbar-fixed-top&quot;<br>  role=&quot;navigation&quot;<br>&gt;<br>  &lt;div class=&quot;container-fluid&quot;&gt;<br>    &lt;div class=&quot;navbar-header&quot;&gt;<br>      &lt;button type=&quot;button&quot;<br>        class=&quot;navbar-toggle&quot;<br>        data-toggle=&quot;collapse&quot;<br>        data-target=&quot;#navbar-collapse&quot;<br>      &gt;<br>        &lt;span class=&quot;sr-only&quot;&gt;Toggle navigation&lt;/span&gt;<br>        &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>        &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>        &lt;span class=&quot;icon-bar&quot;&gt;&lt;/span&gt;<br>      &lt;/button&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbar-collapse&quot;&gt;<br>      &lt;ul class=&quot;nav navbar-nav&quot;&gt;<br>        &lt;li class=&quot;@yield(&quot;navigation/layout/class&quot;)&quot;&gt;<br>          &lt;a href=&quot;{{ URL::route(&quot;admin/layout/index&quot;) }}&quot;&gt;<br>            Layouts<br>          &lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li class=&quot;@yield(&quot;navigation/page/class&quot;)&quot;&gt;<br>          &lt;a href=&quot;{{ URL::route(&quot;admin/page/index&quot;) }}&quot;&gt;<br>            Pages<br>          &lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/nav&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/include/navigation.blade.php</strong>.</blockquote><pre>&lt;ol class=&quot;breadcrumb&quot;&gt;<br>  &lt;li&gt;<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/page/index&quot;) }}&quot;&gt;<br>      List Pages<br>    &lt;/a&gt;<br>  &lt;/li&gt;<br>  &lt;li&gt;<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/page/add&quot;) }}&quot;&gt;<br>      Add New Page<br>    &lt;/a&gt;<br>  &lt;/li&gt;<br>&lt;/ol&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/include/<br>page/navigation.blade.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/page/class&quot;)<br>  active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/page/navigation&quot;)<br>  @if (count($pages))<br>    &lt;table class=&quot;table table-striped&quot;&gt;<br>      &lt;thead&gt;<br>        &lt;tr&gt;<br>          &lt;th class=&quot;wide&quot;&gt;<br>            File<br>          &lt;/th&gt;<br>          &lt;th class=&quot;narrow&quot;&gt;<br>            Actions<br>          &lt;/th&gt;<br>        &lt;/tr&gt;<br>      &lt;/thead&gt;<br>      &lt;tbody&gt;<br>        @foreach ($pages as $page)<br>          @if ($page[&quot;type&quot;] == &quot;file&quot;)<br>            &lt;tr&gt;<br>              &lt;td class=&quot;wide&quot;&gt;<br>                &lt;a href=&quot;{{ $edit . $page[&quot;basename&quot;] }}&quot;&gt;<br>                  {{ $page[&quot;basename&quot;] }}<br>                &lt;/a&gt;<br>              &lt;/td&gt;<br>              &lt;td class=&quot;narrow actions&quot;&gt;<br>                &lt;a href=&quot;{{ $edit . $page[&quot;basename&quot;] }}&quot;&gt;<br>                  &lt;i class=&quot;glyphicon glyphicon-pencil&quot;&gt;&lt;/i&gt;<br>                &lt;/a&gt;<br>                &lt;a href=&quot;{{ $delete . $page[&quot;basename&quot;] }}&quot;&gt;<br>                  &lt;i class=&quot;glyphicon glyphicon-trash&quot;&gt;&lt;/i&gt;<br>                &lt;/a&gt;<br>              &lt;/td&gt;<br>            &lt;/tr&gt;<br>          @endif<br>        @endforeach<br>      &lt;/tbody&gt;<br>    &lt;/table&gt;<br>  @else<br>    No pages yet.<br>    &lt;a href=&quot;{{ URL::route(&quot;admin/page/add&quot;) }}&quot;&gt;<br>      create one now!<br>    &lt;/a&gt;<br>  @endif<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/page/index.blade.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/page/class&quot;)<br>  active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/page/navigation&quot;)<br>  &lt;form role=&quot;form&quot; method=&quot;post&quot;&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;name&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;name&quot;<br>        name=&quot;name&quot;<br>        placeholder=&quot;new-page&quot;<br>        value=&quot;{{ Input::old(&quot;name&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;route&quot;&gt;Route&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;route&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;route&quot;<br>        name=&quot;route&quot;<br>        placeholder=&quot;/new-page&quot;<br>        value=&quot;{{ Input::old(&quot;route&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;layout&quot;&gt;Layout&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;layout&quot;) }}<br>      &lt;/span&gt;<br>      {{ Form::select(<br>        &quot;layout&quot;,<br>        $layouts,<br>        Input::old(&quot;layout&quot;), <br>        [<br>          &quot;id&quot; =&gt; &quot;layout&quot;,<br>          &quot;class&quot; =&gt; &quot;form-control&quot;<br>        ]<br>      ) }}<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;title&quot;&gt;Meta Title&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;title&quot;<br>        name=&quot;title&quot;<br>        value=&quot;{{ Input::old(&quot;title&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;description&quot;&gt;Meta Description&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;description&quot;<br>        name=&quot;description&quot;<br>        value=&quot;{{ Input::old(&quot;description&quot;) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;code&quot;&gt;Code&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;code&quot;) }}<br>      &lt;/span&gt;<br>      &lt;textarea<br>        class=&quot;form-control&quot;<br>        id=&quot;code&quot;<br>        name=&quot;code&quot;<br>        rows=&quot;5&quot;<br>        placeholder=&quot;&amp;lt;div&amp;gt;Hello world&amp;lt;/div&amp;gt;&quot;<br>      &gt;{{ Input::old(&quot;code&quot;) }}&lt;/textarea&gt;<br>    &lt;/div&gt;<br>    &lt;input<br>      type=&quot;submit&quot;<br>      name=&quot;save&quot;<br>      class=&quot;btn btn-default&quot;<br>      value=&quot;Save&quot;<br>    /&gt;<br>  &lt;/form&gt;<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/page/add.blade.php</strong>.</blockquote><pre>@extends(&quot;admin/layout&quot;)<br>@section(&quot;navigation/page/class&quot;)<br> active<br>@stop<br>@section(&quot;content&quot;)<br>  @include(&quot;admin/include/page/navigation&quot;)<br>  &lt;form role=&quot;form&quot; method=&quot;post&quot;&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;name&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;name&quot;<br>        name=&quot;name&quot;<br>        placeholder=&quot;new-page&quot;<br>        value=&quot;{{ Input::old(&quot;name&quot;, $name) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;route&quot;&gt;Route&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;route&quot;) }}<br>      &lt;/span&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;route&quot;<br>        name=&quot;route&quot;<br>        placeholder=&quot;/new-page&quot;<br>        value=&quot;{{ Input::old(&quot;route&quot;, $route) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;layout&quot;&gt;Layout&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;layout&quot;) }}<br>      &lt;/span&gt;<br>      {{ Form::select(<br>        &quot;layout&quot;,<br>        $layouts,<br>        Input::old(&quot;layout&quot;, $layout), <br>        [<br>          &quot;id&quot; =&gt; &quot;layout&quot;,<br>          &quot;class&quot; =&gt; &quot;form-control&quot;<br>        ]<br>      ) }}<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;title&quot;&gt;Meta Title&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;title&quot;<br>        name=&quot;title&quot;<br>        value=&quot;{{ Input::old(&quot;title&quot;, $title) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;description&quot;&gt;Meta Description&lt;/label&gt;<br>      &lt;input<br>        type=&quot;text&quot;<br>        class=&quot;form-control&quot;<br>        id=&quot;description&quot;<br>        name=&quot;description&quot;<br>        value=&quot;{{ Input::old(&quot;description&quot;, $description) }}&quot;<br>      /&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;form-group&quot;&gt;<br>      &lt;label for=&quot;code&quot;&gt;Code&lt;/label&gt;<br>      &lt;span class=&quot;help-text text-danger&quot;&gt;<br>        {{ $errors-&gt;first(&quot;code&quot;) }}<br>      &lt;/span&gt;<br>      &lt;textarea<br>        class=&quot;form-control&quot;<br>        id=&quot;code&quot;<br>        name=&quot;code&quot;<br>        rows=&quot;5&quot;<br>        placeholder=&quot;&amp;lt;div&amp;gt;Hello world&amp;lt;/div&amp;gt;&quot;<br>      &gt;{{ Input::old(&quot;code&quot;, $code) }}&lt;/textarea&gt;<br>    &lt;/div&gt;<br>    &lt;input<br>      type=&quot;submit&quot;<br>      name=&quot;save&quot;<br>      class=&quot;btn btn-default&quot;<br>      value=&quot;Save&quot;<br>    /&gt;<br>  &lt;/form&gt;<br>@stop</pre><blockquote>This file should be saved as <strong>app/views/admin/page/edit.blade.php</strong>.</blockquote><p>The views follow a similar pattern to those which we created for managing layout files. The exception is that we add the new layout and route fields to the add and edit page templates. We’ve used the <strong>Form::select()</strong> method to render and select the appropriate layout.</p><pre>Route::any(&quot;admin/page/index&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/page/index&quot;,<br>  &quot;uses&quot; =&gt; &quot;PageController@indexAction&quot;<br>]);<br> <br>Route::any(&quot;admin/page/add&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/page/add&quot;,<br>  &quot;uses&quot; =&gt; &quot;PageController@addAction&quot;<br>]);<br> <br>Route::any(&quot;admin/page/edit&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/page/edit&quot;,<br>  &quot;uses&quot; =&gt; &quot;PageController@editAction&quot;<br>]);<br> <br>Route::any(&quot;admin/page/delete&quot;, [<br>  &quot;as&quot;   =&gt; &quot;admin/page/delete&quot;,<br>  &quot;uses&quot; =&gt; &quot;PageController@deleteAction&quot;<br>]);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>Finally, we add the routes which will allow us to access these pages. With all this in place, we can work on displaying the website content.</p><h3>Displaying Content</h3><p>Aside from our admin pages, we need to be able to catch the all requests, and route them to a single controller/action. We do this by appending the following route to the <strong>routes.php</strong> file:</p><pre>Route::any(&quot;{all}&quot;, [<br>  &quot;as&quot;   =&gt; &quot;index/index&quot;,<br>  &quot;uses&quot; =&gt; &quot;IndexController@indexAction&quot;<br>])-&gt;where(&quot;all&quot;, &quot;.*&quot;);</pre><blockquote>This was extracted from <strong>app/routes.php</strong>.</blockquote><p>We pass all route information to a named parameter (<strong>{all}</strong>), which will be mapped to the <strong>IndexController::indexAction()</strong> method. We also need to specify the regular expression with which the route data should be matched. With <strong>”.*”</strong> we’re telling Laravel to match absolutely anything. This is why this route needs to come right at the end of the <strong>app/routes.php</strong> file.</p><pre>&lt;?php<br> <br>use Formativ\Cms\EngineInterface;<br>use Formativ\Cms\FilesystemInterface;<br> <br>class IndexController<br>extends BaseController<br>{<br>  protected $engine;<br>  protected $filesystem;<br>  <br>  public function __construct(<br>    EngineInterface $engine,<br>    FilesystemInterface $filesystem<br>  )<br>  {<br>    $this-&gt;engine = $engine;<br>    $this-&gt;filesystem = $filesystem;<br>  }<br>  <br>  protected function parseFile($file)<br>  {<br>    return $this-&gt;parseContent(<br>      $this-&gt;filesystem-&gt;read($file[&quot;path&quot;]),<br>      $file<br>    );<br>  } <br>  <br>  protected function parseContent($content, $file = null)<br>  {<br>    $extracted = $this-&gt;engine-&gt;extractMeta($content);<br>    $parsed    = $this-&gt;engine-&gt;parseMeta($extracted[&quot;meta&quot;]);<br>  <br>    return compact(&quot;file&quot;, &quot;content&quot;, &quot;extracted&quot;, &quot;parsed&quot;);<br>  }<br>  <br>  protected function stripExtension($name)<br>  {<br>    return str_ireplace(&quot;.blade.php&quot;, &quot;&quot;, $name);<br>  }<br>  <br>  protected function cleanArray($array)<br>  {<br>    return array_filter($array, function($item) {<br>      return !empty($item);<br>    });<br>  }<br>  <br>  public function indexAction($route = &quot;/&quot;)<br>  {<br>    $pages = $this-&gt;filesystem-&gt;listContents(&quot;pages&quot;);<br>  <br>    foreach ($pages as $page)<br>    {<br>      if ($page[&quot;type&quot;] == &quot;file&quot;)<br>      {<br>        $page = $this-&gt;parseFile($page);<br>  <br>        if ($page[&quot;parsed&quot;][&quot;route&quot;] == $route)<br>        {<br>          $basename   = $page[&quot;file&quot;][&quot;basename&quot;];<br>          $name       = &quot;pages/extracted/&quot; . $basename;<br>          $layout     = $page[&quot;parsed&quot;][&quot;layout&quot;];<br>          $layoutName = &quot;layouts/extracted/&quot; . $layout;<br>          $extends    = $this-&gt;stripExtension($layoutName);<br> <br>          $template = &quot;<br>            <a href="http://twitter.com/extends">@extends</a>(&#39;&quot; . $extends . &quot;&#39;)<br>            <a href="http://twitter.com/section">@section</a>(&#39;page&#39;)<br>              &quot; . $page[&quot;extracted&quot;][&quot;template&quot;] . &quot;<br>            <a href="http://twitter.com/stop">@stop</a><br>          &quot;;<br>  <br>          $this-&gt;filesystem-&gt;put($name, trim($template));<br>  <br>          $layout = &quot;layouts/&quot; . $layout;<br>  <br>          $layout = $this-&gt;parseContent(<br>            $this-&gt;filesystem-&gt;read($layout)<br>          );<br>  <br>          $this-&gt;filesystem-&gt;put(<br>            $layoutName,<br>            $layout[&quot;extracted&quot;][&quot;template&quot;]<br>          );<br>  <br>          $data = array_merge(<br>            $this-&gt;cleanArray($layout[&quot;parsed&quot;]),<br>            $this-&gt;cleanArray($page[&quot;parsed&quot;])<br>          );<br>  <br>          return View::make(<br>            $this-&gt;stripExtension($name),<br>            $data<br>          );<br>        }<br>      }<br>    }<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><p>In the IndexController class, we’ve injected the same two dependencies: <strong>$filesystem</strong> and <strong>$engine</strong>. We need these to fetch and extract the template data.</p><p>We begin by fetching all the files in the <strong>app/views/pages</strong> directory. We iterate through them filtering out all the returned items which have a <strong>type</strong> of <strong>file</strong>. From these, we fetch the metadata and check if the route defined matches that which is being requested.</p><p>If there is a match, we extract the template data and save it to a new file, resembling <strong>app/views/pages/extracted/[original name]</strong>. We then fetch the layout defined in the metadata, performing a similar transformation. We do this because we still want to run the templates through Blade (so that <strong>@extends</strong>, <strong>@include</strong>, <strong>@section</strong> etc.) all still work as expected.</p><p>We filter the page metadata and the layout metadata, to omit any items which do not have values, and we pass the merged array to the view. Blade takes over and we have a rendered view!</p><h3>Extending The CMS</h3><p>We’ve implemented the simplest subset of October functionality. There’s a lot more going on that I would love to implement, but we’ve run out of time to do so. If you’ve found this project interesting, perhaps you would like to take a swing at implementing partials (they’re not much work if you prevent them from having metadata). Or perhaps you’re into JavaScript and want to try your hand at emulating some of the Ajax framework magic that October’s got going on…</p><blockquote>You can learn more about OctoberCMS’s features at: <a href="http://octobercms.com/docs/cms/themes"><strong>http://octobercms.com/docs/cms/themes</strong></a>.</blockquote><h3>Conclusion</h3><p>If you enjoyed this tutorial; it would be helpful if you could click the <strong>Recommend</strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/150/1*05D0E3dhjsTkABDRORHt0w.png" /></figure><p>This tutorial comes <a href="https://leanpub.com/laravel4cookbook"><strong>from a book I’m writing</strong></a>. If you like it and want to support future tutorials; please consider buying it. Half of all sales go to Laravel.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4bca98a74f4d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/laravel-4-file-based-cms-4bca98a74f4d">Laravel 4 File-Based CMS</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 4 Embedded Systems]]></title>
            <link>https://medium.com/laravel-4/laravel-4-embedded-systems-8b18feccd3c?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/8b18feccd3c</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Thu, 09 Jan 2014 07:51:22 GMT</pubDate>
            <atom:updated>2014-01-19T18:32:42.023Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A Comprehensive Tutorial</h4><p>What do you think of when you hear the name Laravel? Laravel 4 powers an increasing number of websites, but that’s not the only place it can be used. In this tutorial we’re going to use it to control embedded systems.</p><blockquote>While much effort has been spent in the pursuit of accuracy; there’s a good chance you could stumble across a curly quote in a code listing, or some other egregious errata. Please make a note and I will fix where needed.</blockquote><blockquote>I have also uploaded this code to Github. You need simply follow the configuration instructions in this tutorial, after downloading the source code, and the application should run fine.</blockquote><blockquote>This assumes, of course, that you know how to do that sort of thing. If not; this shouldn’t be the first place you learn about making PHP applications.</blockquote><blockquote><a href="https://github.com/formativ/tutorial-laravel-4-embedded-systems"><strong>https://github.com/formativ/tutorial-laravel-4-embedded-systems</strong></a></blockquote><blockquote>If you spot differences between this tutorial and that source code, please raise it here or as a GitHub issue. Your help is greatly appreciated.</blockquote><h3>Gathering Parts</h3><p>This tutorial demonstrates the use of an Arduino and a web cam. There are other smaller parts, but these are the main ones. The Arduino needs firmata installed (explained later) and the webcam needs to be compatible with Linux and Motion (installed later).</p><p>We will go over using things like LEDs and resistors, but none of those are particularly important. We will also go over using servos and these are important. You can find the bracket I use at: <a href="https://www.sparkfun.com/products/10335"><strong>https://www.sparkfun.com/<br>products/10335</strong></a> and the servos I use at: <a href="https://www.sparkfun.com/products/9065"><strong>https://www.sparkfun.com/<br>products/9065</strong></a>. You can get these parts, and an Arduino Uno, for less than $55.</p><h3>Installing Dependencies</h3><p>We’re developing a Laravel 4 application which has lots of server-side aspects; but there’s also an interactive interface. There be scripts!</p><p>For this; we’re using Bootstrap and jQuery. Download Bootstrap at: <a href="http://getbootstrap.com/"><strong>http://getbootstrap.com/</strong></a> and unpack it into your public folder. Where you put the individual files makes little difference, but I have put the scripts in <strong>public/js</strong>, the stylesheets in <strong>public/css</strong> and the fonts in <strong>public/fonts</strong>. Where you see those paths in my source code; you should substitute them with your own.</p><p>Next up, download jQuery at: <a href="http://jquery.com/download/"><strong>http://jquery.com/download/</strong></a> and unpack it into your public folder.</p><p>For the server-side portion of dependencies, we need to download a library called Ratchet. I’ll explain it shortly, but in the meantime we need to add it to our <strong>composer.json</strong> file:</p><pre>&quot;require&quot; : {<br>    &quot;laravel/framework&quot; : &quot;4.0.*&quot;,<br>    &quot;cboden/Ratchet&quot;    : &quot;0.3.*&quot;<br>},</pre><blockquote>This was extracted from <strong>composer.json</strong>.</blockquote><p>Follow that up with:</p><pre>composer update</pre><blockquote>Ratchet isn’t built specifically for Laravel 4, so there are no service providers for us to add.</blockquote><p>We’ll now have access to the Ratchet library for client-server communication, Bootstrap for styling the interface and jQuery for connecting these two things together.</p><h3>Note About Errata</h3><p>I love all things Arduino and Raspberry Pi. When I’m not programming; I’m trying to build things (in the real world) which can interact with my programming. Don’t let that fool you though: I am not an expert in electronics. There are bound to be better ways to do all of this stuff.</p><p>If you can’t get a specific program installed because apt or permissions or network settings or aliens; I cannot help you. I have neither the time nor the experience to guide you through the many potential problems. Hit me up on Twitter and, if I have not yet already done so, I will connect you to a forum where you can discuss this with other (smarter) people.</p><h3>Creating An Interface</h3><p>The first step to creating an interface is returning static HTML for a web request. We handle this by creating a route, a controller and a view:</p><pre>&lt;?php<br><br>Route::get(&quot;/&quot;, [<br>  &quot;as&quot;   =&gt; &quot;index/index&quot;,<br>  &quot;uses&quot; =&gt; &quot;IndexController@indexAction&quot;<br>]);</pre><blockquote>This file should be saved as <strong>app/routes.php</strong>.</blockquote><pre>&lt;?php<br><br>class IndexController<br>extends BaseController<br>{<br>  public function indexAction()<br>  {<br>    return View::make(&quot;index/index&quot;);<br>  }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;UTF-8&quot; /&gt;<br>    &lt;title&gt;Laravel 4 Embedded Systems&lt;/title&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.3.0.0.css&quot; /&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.theme.3.0.0.css&quot; /&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/shared.css&quot; /&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;div class=&quot;container&quot;&gt;<br>      &lt;div class=&quot;row&quot;&gt;<br>        &lt;div class=&quot;col-md-12&quot;&gt;<br>          &lt;h1&gt;Laravel 4 Embedded Systems&lt;/h1&gt;<br>          &lt;input type=&quot;number&quot; max=&quot;255&quot; min=&quot;0&quot; name=&quot;led&quot; /&gt;<br>          &lt;input type=&quot;number&quot; min=&quot;0&quot; name=&quot;sensor&quot; /&gt;<br>          &lt;input type=&quot;number&quot; max=&quot;180&quot; min=&quot;0&quot; name=&quot;x&quot; /&gt;<br>          &lt;input type=&quot;number&quot; max=&quot;180&quot; min=&quot;0&quot; name=&quot;y&quot; /&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>    &lt;script src=&quot;/js/jquery.1.9.1.js&quot;&gt;&lt;/script&gt;<br>    &lt;script src=&quot;/js/shared.js&quot;&gt;&lt;/script&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><blockquote>This file should be saved as <strong>app/views/index/index.blade.php</strong>.</blockquote><p>If you’ve created all of these files (and configured your web server to serve the Laravel application) then you should see a set of controls in your browser, when you visit <strong>/</strong>. Before we move on; let’s just quickly set up a socket server for this interface to talk to the PHP server with….</p><h3>Creating A Socket Server</h3><p><a href="https://medium.com/laravel-4/eaa550829538"><strong>We’ve covered this topic before</strong></a>; so I won’t go into too much detail. We’re going to set up a Ratchet socket server, to pass messages back and forth between the interface and the PHP server.</p><pre>try {<br>  if (!WebSocket) {<br>      console.log(&quot;no websocket support&quot;);<br>  } else {<br><br>    var socket = new WebSocket(&quot;ws://127.0.0.1:8081/&quot;);<br><br>    socket.addEventListener(&quot;open&quot;, function (e) {<br>      console.log(&quot;open: &quot;, e);<br>    });<br><br>    socket.addEventListener(&quot;error&quot;, function (e) {<br>      // console.log(&quot;error: &quot;, e);<br>    });<br><br>    socket.addEventListener(&quot;message&quot;, function (e) {<br>      var data = JSON.parse(e.data);<br>      console.log(&quot;message: &quot;, data);<br>    });<br><br>    // console.log(&quot;socket:&quot;, socket);<br><br>    window.socket = socket;<br><br>  }<br>} catch (e) {<br>  // console.log(&quot;exception: &quot; + e);<br>}</pre><blockquote>This file should be saved as <strong>public/js/shared.js</strong>.</blockquote><pre>&lt;?php<br><br>namespace Formativ\Embedded;<br><br>use Evenement\EventEmitter;<br>use Illuminate\Support\ServiceProvider;<br><br>class EmbeddedServiceProvider<br>extends ServiceProvider<br>{<br>  protected $defer = true;<br><br>  public function register()<br>  {<br>    $this-&gt;app-&gt;bind(&quot;embedded.emitter&quot;, function()<br>    {<br>      return new EventEmitter();<br>    });<br><br>    $this-&gt;app-&gt;bind(&quot;embedded.command.serve&quot;, function()<br>    {<br>      return new Command\Serve(<br>        $this-&gt;app-&gt;make(&quot;embedded.socket&quot;)<br>      );<br>    });<br><br>    $this-&gt;app-&gt;bind(&quot;embedded.socket&quot;, function()<br>    {<br>      return new Socket(<br>        $this-&gt;app-&gt;make(&quot;embedded.emitter&quot;)<br>      );<br>    });<br><br>    $this-&gt;commands(<br>      &quot;embedded.command.serve&quot;<br>    );<br>  }<br><br>  public function provides()<br>  {<br>    return [<br>      &quot;embedded.emitter&quot;,<br>      &quot;embedded.command.serve&quot;,<br>      &quot;embedded.socket&quot;<br>    ];<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/embedded/src/<br>Formativ/Embedded/EmbeddedServiceController.php</strong>.</blockquote><pre>&lt;?php<br><br>namespace Formativ\Embedded;<br><br>use Evenement\EventEmitterInterface;<br>use Ratchet\MessageComponentInterface;<br><br>interface SocketInterface<br>extends MessageComponentInterface<br>{<br>    public function getEmitter();<br>    public function setEmitter(EventEmitterInterface $emitter);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/embedded/src/<br>Formativ/Embedded/SocketInterface.php</strong>.</blockquote><pre>&lt;?php<br><br>namespace Formativ\Embedded;<br><br>use Evenement\EventEmitterInterface as Emitter;<br>use Exception;<br>use Ratchet\ConnectionInterface as Connection;<br><br>class Socket<br>implements SocketInterface<br>{<br>  protected $emitter;<br><br>  protected $connection;<br><br>  public function getEmitter()<br>  {<br>    return $this-&gt;emitter;<br>  }<br><br>  public function setEmitter(Emitter $emitter)<br>  {<br>    $this-&gt;emitter = $emitter;<br>  }<br><br>  public function __construct(Emitter $emitter)<br>  {<br>    $this-&gt;emitter = $emitter;<br>  }<br><br>  public function onOpen(Connection $connection)<br>  {<br>    $this-&gt;connection = $connection;<br>    $this-&gt;emitter-&gt;emit(&quot;open&quot;);<br>  }<br><br>  public function onMessage(Connection $connection, $message)<br>  {<br>    $this-&gt;emitter-&gt;emit(&quot;message&quot;, [$message]);<br>  }<br><br>  public function onClose(Connection $connection)<br>  {<br>    $this-&gt;connection = null;<br>  }<br><br>  public function onError(Connection $connection, Exception $e)<br>  {<br>    $this-&gt;emitter-&gt;emit(&quot;error&quot;, [$e]);<br>  }<br><br>  public function send($message)<br>  {<br>    if ($this-&gt;connection)<br>    {<br>      $this-&gt;connection-&gt;send($message);<br>    }<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/embedded/src/<br>Formativ/Embedded/Socket.php</strong>.</blockquote><blockquote>The <strong>Socket</strong> class only has room for a single connection. Feel free to change this so that it can handle any number of controlling connections.</blockquote><pre>&lt;?php<br><br>namespace Formativ\Embedded\Command;<br><br>use Formativ\Embedded\SocketInterface;<br>use Illuminate\Console\Command;<br>use Ratchet\ConnectionInterface;<br>use Ratchet\Http\HttpServer;<br>use Ratchet\Server\IoServer;<br>use Ratchet\WebSocket\WsServer;<br>use Symfony\Component\Console\Input\InputOption;<br>use Symfony\Component\Console\Input\InputArgument;<br><br>class Serve<br>extends Command<br>{<br>  protected $name = &quot;embedded:serve&quot;;<br><br>  protected $description = &quot;Creates a firmata socket server.&quot;;<br><br>  public function __construct(SocketInterface $socket)<br>  {<br>    parent::__construct();<br><br>    $this-&gt;socket = $socket;<br><br>    $socket-&gt;getEmitter()-&gt;on(&quot;message&quot;, function($message) {<br>      $this-&gt;info(&quot;Message: &quot; . $message . &quot;.&quot;);<br>    });<br><br>    $socket-&gt;getEmitter()-&gt;on(&quot;error&quot;, function($e) {<br>      $this-&gt;line(&quot;Exception: &quot; . $e-&gt;getMessage() . &quot;.&quot;);<br>    });<br>  }<br><br>  public function fire()<br>  {<br>    $port = (integer) $this-&gt;option(&quot;port&quot;);<br><br>    $server = IoServer::factory(<br>      new HttpServer(<br>        new WsServer(<br>          $this-&gt;socket<br>        )<br>      ),<br>      $port<br>    );<br><br>    $this-&gt;info(&quot;Listening on port &quot; . $port . &quot;.&quot;);<br>    $server-&gt;run();<br>  }<br><br>  protected function getOptions()<br>  {<br>    return [<br>      [<br>        &quot;port&quot;,<br>        null,<br>        InputOption::VALUE_REQUIRED,<br>        &quot;Port to listen on.&quot;,<br>        8081<br>      ]<br>    ];<br>  }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/embedded/src/<br>Formativ/Embedded/Command/Serve.php</strong>.</blockquote><p>These four files create a means with which we can start a socket server, listen for messages and respond to them. The <strong>shared.js</strong> file connects to this server and allows us to send and receive these messages. It’s simpler than last time; but sufficient for our needs!</p><h3>Connection To Arduino</h3><p>Real-time communication is typically done through a software layer called Firmata. There are Firmata libraries for many programming languages, though PHP is not one of them. While there is one library listed on the official Firmata website, I could not get it to work.</p><p>So we’re going to create our own simple Firmata-like protocol. I managed to put one together that handles analog reads, analog writes and servo control. It does this by parsing strings received over serial connection. This is what it looks like:</p><pre>#include &lt;Servo.h&gt;<br> <br>String buffer = &quot;&quot;;<br>String parts[3];<br>Servo servos[13];<br>int index = 0;<br> <br>void setup()<br>{<br>  Serial.begin(9600);<br>  buffer.reserve(200);<br>}<br> <br>void loop()<br>{<br> <br>}<br> <br>void handle()<br>{ <br>  int pin = parts[1].toInt();<br>  int value = parts[2].toInt();<br> <br>  if (parts[0] == &quot;pinMode&quot;)<br>  {<br>    if (parts[2] == &quot;output&quot;)<br>    {<br>      pinMode(pin, OUTPUT);<br>    }<br> <br>    if (parts[2] == &quot;servo&quot;)<br>    {<br>      servos[pin].attach(pin);<br>    }<br>  }<br> <br>  if (parts[0] == &quot;digitalWrite&quot;)<br>  {<br>    if (parts[2] == &quot;high&quot;)<br>    {<br>      digitalWrite(pin, HIGH);<br>    }<br>    else<br>    {<br>      digitalWrite(pin, LOW);<br>    }<br>  }<br> <br>  if (parts[0] == &quot;analogWrite&quot;)<br>  {<br>    analogWrite(pin, value);<br>  }<br> <br>  if (parts[0] == &quot;servoWrite&quot;)<br>  {<br>    servos[pin].write(value);<br>  }<br> <br>  if (parts[0] == &quot;analogRead&quot;)<br>  {<br>    value = analogRead(pin);<br>  }<br> <br>  Serial.print(parts[0] + &quot;,&quot; + parts[1] + &quot;,&quot; + value + &quot;.\n&quot;);<br>}<br> <br>void serialEvent()<br>{<br>  while (Serial.available())<br>  {<br>    char in = (char) Serial.read();<br> <br>    if (in == &#39;.&#39; || in == &#39;,&#39;)<br>    {<br>      parts[index] = String(buffer);<br>      buffer = &quot;&quot;;<br> <br>      index++;<br> <br>      if (index &gt; 2)<br>      {<br>        index = 0;<br>      }<br>    }<br>    else<br>    {<br>      buffer += in;<br>    }<br> <br>    if (in == &#39;.&#39;)<br>    {<br>      index = 0;<br>      buffer = &quot;&quot;;<br>      handle();<br>    }<br>  }<br>}</pre><blockquote>This code needs to be uploaded to the Arduino you’re working with for any communication between our server and the Arduino to take place.</blockquote><p>The Arduino sketch is only one side of the puzzle. We also need to modify our socket server to communicate with the Arduino:</p><pre>public function __construct(SocketInterface $socket)<br>{<br>  parent::__construct();<br>  <br>  $this-&gt;socket = $socket;<br>  <br>  $socket-&gt;getEmitter()-&gt;on(&quot;message&quot;, function($message)<br>  { <br>    fwrite($this-&gt;device, $message);<br>  <br>    $data = trim(stream_get_contents($this-&gt;device));<br>    $this-&gt;info($data);<br>  <br>    $this-&gt;socket-&gt;send($data);<br>  });<br>  <br>  $socket-&gt;getEmitter()-&gt;on(&quot;error&quot;, function($e)<br>  {<br>    $this-&gt;line(&quot;exception: &quot; . $e-&gt;getMessage() . &quot;.&quot;);<br>  });<br>}<br> <br>public function fire()<br>{<br>  $this-&gt;device = fopen($this-&gt;argument(&quot;device&quot;), &quot;r+&quot;);<br>  stream_set_blocking($this-&gt;device, 0);<br>  <br>  $port = (integer) $this-&gt;option(&quot;port&quot;);<br>  <br>  $server = IoServer::factory(<br>    new HttpServer(<br>      new WsServer(<br>        $this-&gt;socket<br>      )<br>    ),<br>    $port<br>  );<br>  <br>  $this-&gt;info(&quot;Listening on port &quot; . $port . &quot;.&quot;);<br>  $server-&gt;run();<br>}<br> <br>protected function getArguments()<br>{<br>  return [<br>    [<br>      &quot;device&quot;,<br>      InputArgument::REQUIRED,<br>      &quot;Device to use.&quot;<br>    ]<br>  ];<br>}<br> <br>public function __destruct()<br>{<br>  if (is_resource($this-&gt;device)) {<br>    fclose($this-&gt;device); <br>  }<br>}</pre><p>We’re using PHP file read/write methods to communicate with the Arduino. In our <strong>fire()</strong> method, we open a connection to the Arduino and store it in <strong>$this-&gt;device</strong>. We also set the stream to non-blocking. This is so that read requests to the Arduino don’t wait until data is returned before allowing the rest of the PHP processing to take place.</p><p>In the <strong>__construct()</strong> method we also modify the message event listener to write the incoming message (from the browser) to the Arduino. We read any info the Arduino has and send it back to the socket. This effectively creates a bridge between the Arduino and the socket.</p><p>We’ve also added two additional methods. The <strong>getArguments()</strong> method stipulates a required device argument. We want the serve command to be able to use any device name, so this argument makes sense. We’ve also added a <strong>__destruct()</strong> method to close the connection to the Arduino.</p><p>Finally, we need to add some JavaScript to read and write from the Arduino:</p><pre>try {<br>  if (!WebSocket) {<br>    console.log(&quot;no websocket support&quot;);<br>  } else {<br>  <br>    var socket = new WebSocket(&quot;ws://127.0.0.1:8081/&quot;);<br>    var sensor = $(&quot;[name=&#39;sensor&#39;]&quot;);<br>    var led = $(&quot;[name=&#39;led&#39;]&quot;);<br>    var x = $(&quot;[name=&#39;x&#39;]&quot;);<br>    var y = $(&quot;[name=&#39;y&#39;]&quot;);<br>  <br>    socket.addEventListener(&quot;open&quot;, function (e) {<br>      socket.send(&quot;pinMode,6,output.&quot;);<br>      socket.send(&quot;pinMode,3,servo.&quot;);<br>      socket.send(&quot;pinMode,5,servo.&quot;);<br>    });<br>  <br>    socket.addEventListener(&quot;error&quot;, function (e) {<br>      // console.log(&quot;error: &quot;, e);<br>    });<br>  <br>    socket.addEventListener(&quot;message&quot;, function (e) {<br>  <br>    var data = e.data;<br>    var parts = data.split(&quot;,&quot;);<br>  <br>    if (parts[0] == &quot;analogRead&quot;) {<br>      sensor.val(parseInt(parts[2], 10));<br>    }<br>  <br>  });<br>  <br>  window.socket = socket;<br>  <br>  led.on(&quot;change&quot;, function() {<br>    socket.send(&quot;analogWrite,6,&quot; + led.val() + &quot;.&quot;);<br>  });<br>  <br>  x.on(&quot;change&quot;, function() {<br>    socket.send(&quot;servoWrite,5,&quot; + x.val() + &quot;.&quot;);<br>  });<br>  <br>  y.on(&quot;change&quot;, function() {<br>    socket.send(&quot;analogWrite,3,&quot; + y.val() + &quot;.&quot;);<br>  });<br>  <br>  setInterval(function() {<br>    socket.send(&quot;analogRead,0,void.&quot;);<br>  }, 1000);<br>  <br>  }<br>} catch (e) {<br>  // console.log(&quot;exception: &quot; + e);<br>}</pre><p>This script does a number of cool things. When it loads, we set the pinMode of the LED pin (6) and the two servo pins (3, 5) to the correct modes. We also attach a message event listener to receive and parse messages from the Arduino.</p><p>We then attach some event listeners for the input elements in the interface. These will respond to changes in value by sending signals to the Arduino to adjust its various pin values.</p><p>Finally, we set a timer to ping the Arduino with analogRead requests on the sensor pin. Thanks to the message event listener, the return values will be received and reflected in the sensor input field.</p><h3>Spinning Up</h3><p>To connect to the Arduino, you should run the following command:</p><pre>php artisan embedded:serve /dev/cu.usbmodem1411</pre><p>On my system, the Arduino is reflected as <strong>/dev/cu.usbmodem1411</strong> by this may differ on yours. For instance, my Ubuntu machine shows it as <strong>/dev/ttyACM0</strong>. The easiest way to find out what it is, is to unplug it, then run the following command:</p><pre>ls /dev/tty*</pre><p>Look over the list to familiarise yourself with the devices you see there. The plug the Arduino in, wait a couple seconds and run that command again. This time you should see a new addition to the list of devices. This is most likely your Arduino.</p><blockquote>You may be wondering why my device starts with <strong>cu</strong> and not <strong>tty</strong>. I first tried the <strong>tty</strong> version of the Arduino and it would cause the server to hang. I experimented and found that the <strong>cu</strong> version let me connect and communicate with the Arduino. Go figure…</blockquote><p>I usually like to include Vagrant configurations for running these projects. This time I found it confusing to try and communicate with an Arduino through the host machine. So instead I decided to try the server.php script bundled with Laravel applications. Turns out you can run the server with the following command:</p><pre>php -S 127.0.0.1:8080 -t ./public server.php</pre><p>Now you can go to <a href="http://127.0.0.1:8080/"><strong>http://127.0.0.1:8080</strong></a> and see the interface we created earlier. You need to start the serve command successfully before this interface will work. If everything is running correctly, you should immediately start to see the sensor values being updated in the interface.</p><p>You can now adjust the LED input to control the brightness of the LED, and the x and y inputs to control the rotation of the servos.</p><h3>Adding A Webcam</h3><p>Streaming video from a web cam can be tricky, so we’re going to take time-lapse photos instead…</p><h4>Installing ImageSnap On OSX</h4><p>The utility we’ll be using on OSX is called ImageSnap. We can install it with homebrew:</p><pre>brew install imagesnap</pre><p>Once that’s installed; we can periodically take photos with it by using a command resembling:</p><pre>imagesnap -d &quot;Microsoft LifeCam&quot; -w 3</pre><p>The web cam I am using is <strong>Microsoft LifeCam</strong> but you can find the appropriate one for you with the command:</p><pre>imagesnap -l</pre><p>I also had to add some warm-up time, with the <strong>-w</strong> switch.</p><h4>Installing Streamer On Ubuntu/Debian</h4><p>The utility we’ll be using on Ubuntu/Debian is called Streamer. We can install it with the command:</p><pre>apt-get install -y streamer</pre><p>You can take a photo with the web cam, using the following command:</p><pre>streamer -o snapshot.jpeg</pre><h4>Displaying Photos In The Interface</h4><p>Depending on whether you are using OSX or Ubuntu/Debian; you will need to set up a shell script to periodically take photos. Save them to a web-accessible location and display them with the following changes:</p><pre>&lt;img name=&quot;photo&quot; /&gt;</pre><blockquote>This was extracted from <strong>app/views/index/index.blade.php</strong>.</blockquote><pre>setInterval(function() {<br>  $(&quot;[name=&#39;photo&#39;]&quot;).attr(&quot;src&quot;, &quot;[path to photo]&quot;);<br>}, 1000);</pre><blockquote>This was extracted from <strong>public/js/shared.js</strong>.</blockquote><h3>Conclusion</h3><p>If you enjoyed this tutorial; it would be helpful if you could click the <strong>Recommend</strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/150/1*hkJYYJg2aeBnMt5n6BAjxA.png" /></figure><p>This tutorial comes <a href="https://leanpub.com/laravel4cookbook">from a book I’m writing</a>. If you like it and want to support future tutorials; please consider buying it. Half of all sales go to Laravel.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b18feccd3c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/laravel-4-embedded-systems-8b18feccd3c">Laravel 4 Embedded Systems</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Composer]]></title>
            <link>https://medium.com/laravel-4/composer-ad56bdd66beb?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/ad56bdd66beb</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Tue, 07 Jan 2014 09:02:23 GMT</pubDate>
            <atom:updated>2014-01-07T08:40:58.847Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A crash course</h4><p>Composer is a PHP dependency manager. It lets you define the dependencies your project needs. It mainly downloads dependencies from <a href="http://packagist.org/"><strong>http://packagist.org</strong></a>, but can accept other sources as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sm9uw6uLmu5eNjp7.png" /><figcaption>That guy means business.</figcaption></figure><h3>Installing Composer</h3><p>You can download Composer from <a href="http://getcomposer.org/%20download/"><strong>http://getcomposer.org/<br>download/</strong></a>, where you will also find installation instructions for your system. Here’s I’m using the instructions for installing with Curl. I also move the <strong>composer.phar</strong> file to <strong>/usr/local/bin/composer</strong> so that I can use it from anywhere in the system. If you you don’t have Curl installed, or would prefer another installation approach, there are instructions for you too!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VKu_vGK69j3uQ6Y2.png" /><figcaption>Some bad-ass instructions there…</figcaption></figure><p>Once Composer is installed, you simply need to know the name of the dependency your project requires, and you specify this dependency in a file called <strong>composer.json</strong>. The <strong>composer.json</strong> file also allows you to give your project a name, version and a few other details, which can be used to publish your own Composer packages to Packagist.</p><p>Once you’re happy with the dependencies you have defined; it’s time to install them. Go to your command line and type <strong>composer install</strong>. I typically add<strong>--no-dev</strong> because Composer will install development dependencies by default. If your dependencies will need to build or test themselves, it’s best to leave this out. Installing dependencies may take some time, but any indication of progress is a good sign. Get a cup of coffee and wait it out.</p><p>Composer will automatically download nested dependencies, and the more things it needs to download, the longer it will take to download them. When it’s done; it will also generate a decent class autoloader script. You can immediately start to use auto-loaded dependencies by including <strong>vendor/autoload.php</strong> in your PHP scripts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MeWwGLsbuuseFMk8.png" /><figcaption>Ain’t nobody got time for…not autoloading?!</figcaption></figure><p>The libraries you install with Composer are located in the same <strong>vendor</strong> folder, and Composer will also create a lock file which records the exact versions of your dependencies so that they can be installed in the same way on production servers.</p><p>If your favourite PHP framework installs with Composer; it probably includes this file for you.</p><p>If you are using a framework that doesn’t include the class autoload script, or no framework at all, you just need to include the <strong>vendor/autoloader.php</strong> file to be able to autoload your dependencies.</p><h3>Finding Composer packages</h3><p>There are many ways to find Composer packages, but the easiest are in the <strong>readme.md</strong> files of GitHub repositories, in the library or framework documentation and by searching <a href="http://packagist.org/"><strong>http://packagist.org</strong></a>.</p><p>More and more library authors are creating their own <strong>composer.json</strong> files and submitting their libraries to Packagist. They will usually explain how to use their libraries as a dependency, but it also helps to check any <strong>composer.json</strong> files you see in GitHub repositories.</p><p>Searching Packagist is a little harder if you don’t know what you are looking for, but a search for “http” quickly returned many results (including Guzzle). Give it a shot if you can’t find what you need using GitHub’s search function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FPVKTFJIaxUYeVY8.png" /><figcaption>More packages than a FedEx truck.</figcaption></figure><h3>Submitting your own packages to Packagist</h3><p>Go to <a href="http://packagist.org/"><strong>http://packagist.org</strong></a> and sign in. If you haven’t already got an account, you should create it and link to your GitHub account.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bgaVtbUwmdvRamix.png" /><figcaption>Submit. SUBMIT!</figcaption></figure><p>There’s a big “Submit Package” button. Click it and paste the URL to your GitHub repository in the text field. Submit the form and you should be good to go.</p><p><strong>Conclusion</strong></p><p>If you have any questions; be sure to check out the forums, FAQ and help documents for the thing you’re struggling with.</p><blockquote><a href="https://packagist.org/">https://packagist.org/</a></blockquote><blockquote><a href="http://getcomposer.org/">http://getcomposer.org/</a></blockquote><p>If you found this tutorial helpful, please tell me about it @followchrisp and be sure to recommend it to PHP developers. All PHP developers.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ad56bdd66beb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/composer-ad56bdd66beb">Composer</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 4 Multisites]]></title>
            <link>https://medium.com/laravel-4/laravel-4-multisites-26cdc75e4810?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/26cdc75e4810</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Tue, 07 Jan 2014 09:02:10 GMT</pubDate>
            <atom:updated>2014-01-07T08:12:03.140Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A Comprehensive Tutorial</h4><h3><strong>Introduction</strong></h3><p>Laravel 4 is a huge step forward for the PHP community. It’s beautifully written, full of features and the community is presently exploding. Today we’re going to explore how to build multisite applications with it.</p><blockquote>I have spent two full hours getting the code out of a Markdown document and into Medium. Medium really isn’t designed for tutorials such as this, and while much effort has been spent in the pursuit of accuracy; there’s a good chance you could stumble across a curly quote in a code listing. Please make a note and I will fix where needed.</blockquote><blockquote><em>I have also uploaded this code to Github. You need simply follow the configuration instructions in this tutorial, after downloading the source code, and the application should run fine. This assumes, of course, that you know how to do that sort of thing. If not; this shouldn’t be the first place you learn about making PHP applications.</em></blockquote><blockquote><a href="https://github.com/formativ/tutorial-laravel-4-multisites"><strong>https://github.com/formativ/tutorial-laravel-4-multisites</strong></a></blockquote><blockquote><em>If you spot differences between this tutorial and that source code, please raise it here or as a GitHub issue. Your help is greatly appreciated.</em></blockquote><h3>Installing Laravel 4</h3><p>Laravel 4 uses Composer to manage its dependencies. You can install Composer by following the instructions at <a href="http://getcomposer.org/doc/00-intro.md#installation-nix"><strong>http://getcomposer.org/<br>doc/00-intro.md#installation-nix</strong></a>.</p><p>Once you have Composer working, make a new directory or navigation to an existing directory and install Laravel 4 with the following command:</p><pre>composer create-project laravel/laravel ./ --prefer-dist</pre><p>If you chose not to install Composer globally (though you really should), then the command you use should resemble the following:</p><pre>php composer.phar create-project laravel/laravel ./ --prefer-dist</pre><p>Both of these commands will start the process of installing Laravel 4. There are many dependencies to be sourced and downloaded; so this process may take some time to finish.</p><h3>Note on Operating Systems</h3><p>I work on a Macbook, and deal with Linux servers every day. I seldom interact with Windows-based machines. As a result; most of the work I do is never run on Windows. Remember this as you follow this tutorial — I will not show how to do things on Windows because it’s dead to me. If you are forced to use it; then you have my sympathies.</p><h3>Note on Server Setup</h3><p>We’re going to look at how to create virtual hosts in Apache2 and Nginx, but we won’t look at how to get those systems running in the first place. It’s not something I consider particularly tricky, and the internet is filled with wonderful tutorials on the subject.</p><h3>Note on Dutch</h3><p>I use it in this tutorial, but I don’t really speak it. Google Translate does. Blame Google Translate.</p><h3>Virtual Hosts</h3><p>It may surprise you to know that single domain names do not translate into single web servers. Modern web servers have the ability to load many different domains, and these domains are often referred to as Virtual Hosts or Virtual Domains. We’re going to see how to use them with Laravel 4.</p><h4>Adding Virtual Host Entries</h4><p>When you type an address into your browser, and hit enter; your browser goes through a set of steps in order to get you the web page you want. First, it queries what’s called a hosts file to see if the address you typed in is a reference to a local IP address. If not found, the browser then queries whatever DNS servers are available to the operating system.</p><p>A DNS server compares an address (e.g. example.com) with a list of IP addresses it has on file. If an IP address is found; the browser’s request is forwarded to it and the web page is delivered.</p><blockquote>This is a rather simplified explanation of the steps involved. You can probably find more details at: <a href="http://en.wikipedia.org/wiki/Internet"><strong>http://en.wikipedia.org/wiki/Internet</strong></a>.</blockquote><p>In a sense; the hosts file acts as a DNS server before any remote requests are made. It follows that the best place to add references to local IP addresses (local web servers) is in the hosts file.</p><p>To do that, open the hosts file:</p><pre>sudo vim /etc/hosts</pre><blockquote>If that command gives you errors then you can try running it without the <strong>sudo</strong> part. Sudo is (simply speaking) a way to run a command at the highest permission level possible; to ensure it executes correctly. If may be that you do not have sufficient privileges to run commands as root (with <strong>sudo</strong>).</blockquote><blockquote>Vim is a teminal-based text editor. If you’re uneasy using it; what we’re after is editing <strong>/etc/hosts</strong>.</blockquote><p>On a new line, at the bottom of /etc/hosts, add:</p><pre>127.0.0.1 dev.tutorial</pre><blockquote>This was extracted from <strong>/etc/hosts </strong>for brevity.</blockquote><p>This line tells the operating system to send requests to <strong>dev.tutorial</strong> to <strong>127.0.0.1</strong>. You’ll want to replace <strong>127.0.0.1</strong> with the IP address of the server you are using for this tutorial (assuming it’s not LAMP/MAMP on your local machine) and <strong>dev.tutorial</strong> with whatever you want the virtual host to be.</p><blockquote>You’ll need to repeat this process for each virtual host you add. If your application has (for example) three different domains pointing to it; then you will need to set three of these up to test them locally.</blockquote><h4>Creating Apache 2 Virtual Hosts</h4><p>Apache2 virtual host entries are created by manipulating the configuration files usually located in <strong>/etc/apache2/sites-available/</strong>. You can edit the <strong>default</strong> file, and add the entries to it, or you can create new files.</p><blockquote>If you choose to create new files for your virtual host entries, then you will need to symlink them to <strong>/etc/apache2/sites-enabled/*</strong> where <strong>*</strong> matches the name of the files you create in the <strong>sites-available</strong> folder.</blockquote><p>The expanded syntax of virtual host entries can be quite a bit to type/maintain; so I have a few go-to lines I usually use:</p><pre>&lt;VirtualHost *:80&gt;<br>    DocumentRoot /var/www/site1<br>    ServerName dev.site1<br>&lt;/VirtualHost&gt;</pre><blockquote>This was extracted from <strong>/etc/apache2/sites-available/default </strong>for brevity.</blockquote><p>The <strong>ServerName</strong> property can be anything you like — it’s the address you will use in your browser. The <strong>DocumentRoot</strong> property needs to point to an existing folder in the filesystem of the web server machine.</p><blockquote>You can learn more about virtual host entries, in Apache2, at: <a href="http://httpd.apache.org/docs/current/vhosts/examples.html"><strong>http://httpd.apache.org/docs/current/vhosts/examples.html</strong></a>.</blockquote><h4>Creating Nginx Virtual Hosts</h4><p>Similar to Apache2, Nginx virtual host entries are created by manipulating the configuration files usually located in <strong>/etc/nginx/sites-available/</strong>. You can edit the <strong>default</strong> file, and add the entries to it, or you can create new files.</p><blockquote>If you choose to create new files for your virtual host entries, then you will need to symlink them to <strong>/etc/nginx/sites-enabled/*</strong> where <strong>*</strong> matches the name of the files you create in the <strong>sites-available</strong> folder.</blockquote><p>The expanded syntax of virtual host entries can be quite a bit to type/maintain; so I have a few go-to lines I usually use:</p><pre>server {<br>    listen 80;<br>    server_name dev.site1;<br>    root /var/www/site1;<br>    index index.php;<br> <br>    location ~ \.php$ {<br>        try_files $uri =404;<br>        fastcgi_split_path_info ^(.+\.php)(/.+)$;<br>        fastcgi_pass unix:/var/run/php5-fpm.sock;<br>        fastcgi_index index.php<br>        include fastcgi_params;<br>    }<br>}</pre><blockquote>This was extracted from <strong>/etc/nginx/sites-available/default </strong>for brevity.</blockquote><blockquote>This site definition assumes you are using PHP5 FPM. Getting this installed and running is out of the scope of this tutorial, but there are plenty of great tutorials on the subject.</blockquote><blockquote>You can learn more about virtual host entries, in Nginx, at: <a href="https://www.digitalocean.com/community/articles/how-to-set-up-nginx-virtual-hosts-server-blocks-on-ubuntu-12-04-lts--3"><strong>https://www.digitalocean.com/community/articles/how-to-set-up-nginx-virtual-hosts-server-blocks-on-ubuntu-12-04-lts--3</strong></a>.</blockquote><h3>Environments</h3><p>Laravel 4 employs a system of execution environments. Think of these as different contexts in which different configuration files will be loaded; determined by the host name of the machine or command flags.</p><p>To illustrate this concept; think of the differences between developing and testing your applications locally and running them on a production server. You will need to target different databases, use different caching schemes etc.</p><p>This can be achieved, deterministically, by setting the environments to match the host names of the machines the code will be executed (local and production respectively) and having different sets of configuration files for each environment.</p><p>When a Laravel 4 application is executed, it will determine the environment that you are running it in, and adjust the path to configuration files accordingly. This approach has two caveats.</p><p>The first caveat is that the PHP configuration files in <strong>app/config</strong> are always loaded and environment-based configuration files are then loaded on top of them. If you have a database set up in <strong>app/config/<br>database.php</strong> and nothing in <strong>app/config/production/database.php</strong>, the global database configuration details will apply.</p><p>The second caveat is that you can override the environment Laravel 4 would otherwise use by supplying an environment flag to artisan commands:</p><pre>php artisan migrate --env=local</pre><p>This will tell artisan to execute the database migrations in the local environment, whether or not the environment you are running it in is local.</p><p>This is important to multisites because Laravel 4 configuration files have the ability to connect to different database, load different views and send different emails based on environmental configuration.</p><h4>Note on Running Commands in Local Environment</h4><p>The moment you add multiple environments to your application, you create the possibility that artisan commands might be run on the incorrect environment.</p><p>Just because Laravel 4 is capable of executing in the correct environment doesn’t mean you will always remember which environment you are in or which environment you should be in…</p><p>Start learning to provide the environment for every artisan command, when you’re working with multiple environments. It’s a good habit to get into.</p><h4>Using Site-Specific Views</h4><p>One of the benefits of multiple environment-specific configuration sets is that we can load different views for different sites. Let’s begin by creating a few:</p><pre>127.0.0.1 dev.www.tutorial-laravel-4-multisites<br>127.0.0.1 dev.admin.tutorial-laravel-4-multisites</pre><blockquote>This was extracted from <strong>/etc/hosts </strong>for brevity.</blockquote><p>You can really create any domains you want, but for this part we’re going to need multiple domains pointing to our testing server so that we can actually see different view files being loaded, based on domain.</p><p>Next, update your <strong>app/bootstrap/start.php</strong> file to include the virtual host domains you’ve created:</p><pre>$env = $app-&gt;detectEnvironment([<br>    &quot;www&quot;   =&gt; [&quot;dev.www.tutorial-laravel-4-multisites&quot;],<br>    &quot;admin&quot; =&gt; [&quot;dev.admin.tutorial-laravel-4-multisites&quot;]<br>]);</pre><blockquote>This was extracted from <strong>/bootstrap/start.php </strong>for brevity.</blockquote><blockquote><a href="http://www.reddit.com/r/PHP/comments/1qm8e3/tutorial_multisites_with_laravel_4/cdecd88"><strong>Phil Sturgeon made an excellent point</strong></a> about not using URL’s to toggle environments, as somebody can easily set a hostname to match your development environment and point it to your production environment to cause unexpected results. This can lead to people gleaning more information than you would expect via debugging information or backtraces.</blockquote><p>If you would rather determine the current environment via a server configuration property; you can pass a callback to the <strong>detectEnvironment()</strong> method:</p><pre>$env = $app-&gt;detectEnvironment(function()<br>{<br>    return Input::server(&quot;environment&quot;, &quot;development&quot;);<br>});</pre><blockquote>This was extracted from <strong>/bootstrap/start.php </strong>for brevity.</blockquote><p>Clean out the <strong>app/views</strong> folder and create <strong>www</strong> and <strong>admin</strong> folders, each with their own <strong>layout.blade.php</strong> and <strong>index/index.blade.php</strong> files.</p><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>    &lt;head&gt;<br>        &lt;meta charset=&quot;utf-8&quot; /&gt;<br>        &lt;title&gt;Laravel 4 Multisites&lt;/title&gt;<br>    &lt;/head&gt;<br>    &lt;body&gt;<br>        <a href="http://twitter.com/yield">@yield</a>(&quot;content&quot;)<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><blockquote>This file should be saved as <strong>app/views/www/layout.blade.php</strong>.</blockquote><pre><a href="http://twitter.com/extends">@extends</a>(&quot;layout&quot;)<br><a href="http://twitter.com/section">@section</a>(&quot;content&quot;)<br>    Welcome to our website!<br><a href="http://twitter.com/stop">@stop</a></pre><blockquote>This file should be saved as <strong>app/views/www/index/index.blade.php</strong>.</blockquote><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>    &lt;head&gt;<br>        &lt;meta charset=&quot;utf-8&quot; /&gt;<br>        &lt;title&gt;Laravel 4 Multisites — Admin&lt;/title&gt;<br>    &lt;/head&gt;<br>    &lt;body&gt;<br>        <a href="http://twitter.com/yield">@yield</a>(&quot;content&quot;)<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><blockquote>This file should be saved as <strong>app/views/admin/layout.blade.php</strong>.</blockquote><pre><a href="http://twitter.com/extends">@extends</a>(&quot;layout&quot;)<br><a href="http://twitter.com/section">@section</a>(&quot;content&quot;)<br>    Please log in to use the admin.<br><a href="http://twitter.com/stop">@stop</a></pre><blockquote>This file should be saved as <strong>app/views/admin/index/index.blade.php</strong>.</blockquote><p>Let’s also prepare the routes and controllers for the rest of the tutorial, by updating both:</p><pre>&lt;?php</pre><pre>Route::any(&quot;/&quot;, [<br>    &quot;as&quot;   =&gt; &quot;index/index&quot;,<br>    &quot;uses&quot; =&gt; &quot;IndexController@indexAction&quot;<br>]);</pre><blockquote>This file should be saved as <strong>app/routes.php</strong>.</blockquote><pre>&lt;?php</pre><pre>class IndexController<br>extends BaseController<br>{<br>    public function indexAction()<br>    {<br>        return View::make(&quot;index/index&quot;);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><p>In order for Laravel to know which views to use for each environment, we should also create the configuration files for the environments.</p><pre>&lt;?php</pre><pre>return [<br>    &quot;paths&quot; =&gt; [app_path() . &quot;/views/www&quot;]<br>];</pre><blockquote>This file should be saved as <strong>app/config/www/view.php</strong>.</blockquote><pre>&lt;?php</pre><pre>return [<br>    &quot;paths&quot; =&gt; [app_path() . &quot;/views/admin&quot;]<br>];</pre><blockquote>This file should be saved as <strong>app/config/admin/view.php</strong>.</blockquote><p>These new configuration files tell Laravel 4 not only to look for views in the default directory but also to look within the environment-specific view folders we’ve set up. Going to each of these virtual host domains should now render different content.</p><p>I would recommend this approach for sites that need to drastically change their appearance in ways mere CSS couldn’t achieve. If, for example, your different sites need to load different HTML or extra components then this is a good approach to take.</p><p>Another good time to use this kind of thing is when you want to separate the markup of a CMS from that of a client-facing website. Both would need access to the same business logic and storage system, but their interfaces should be vastly different.</p><p>I’ve also used this approach when I’ve needed to create basic mobile interfaces and rich interactive interfaces for the same brands…</p><blockquote>You can learn more about environments at: <a href="http://laravel.com/docs/configuration#environment-configuration"><strong>http://laravel.com/docs/<br>configuration#environment-configuration</strong></a>.</blockquote><h4>Using Site-Specific Routes</h4><p>Another aspect to multiple-domain applications is how they can affect (and be affected by) the routes file. Consider the following route groups:</p><pre>Route::group([<br>    &quot;domain&quot; =&gt; &quot;dev.www.tutorial-laravel-4-multisites&quot;<br>], function()<br>{<br>    Route::any(&quot;/about&quot;, function()<br>    {<br>        return &quot;This is the client-facing website.&quot;;<br>    });<br>});</pre><pre>Route::group([<br>    &quot;domain&quot; =&gt; &quot;dev.admin.tutorial-laravel-4-multisites&quot;<br>], function()<br>{<br>    Route::any(&quot;/about&quot;, function()<br>    {<br>        return &quot;This is the admin site.&quot;;<br>    });<br>});</pre><blockquote>This was extracted from <strong>app/routes.php </strong>for brevity.</blockquote><p>Aside from the basic routes Laravel 4 supports, it’s also possible to group routes within route groups. These groups provide an easy way of applying common logic, filtering and targeting specific domains.</p><p>Not only can we explicitly target our different virtual host domains, we can target all subdomains with sub-domain wildcard:</p><pre>Route::group([<br>    &quot;domain&quot; =&gt; &quot;dev.{sub}.tutorial-laravel-4-multisites&quot;<br>], function()<br>{<br>    Route::any(&quot;/whoami&quot;, function($sub)<br>    {<br>        return &quot;You are in the &#39;&quot; . $sub . &quot;&#39; sub-domain.&quot;;<br>    });<br>});</pre><blockquote>This was extracted from <strong>app/routes.php </strong>for brevity.</blockquote><p>This functionality allows some pretty powerful domain-related configuration and logical branching!</p><h3>Translation</h3><p>Laravel 4 includes a translation system that can greatly simplify developing multisites. Translated phrases are stored in configuration files, and these can be returned in views.</p><h4>Using Language Lookups</h4><p>The simplest example of this requires two steps: we need to add the translated phrases and we need to recall them in a view. To begin with; we’re going to add a few English and Dutch phrases to the configuration files:</p><pre>&lt;?php</pre><pre>return [<br>    &quot;instructions&quot; =&gt; &quot;Follow these steps to operate cheese:&quot;,<br>    &quot;step1&quot;        =&gt; &quot;Cut the cheese.&quot;,<br>    &quot;step2&quot;        =&gt; &quot;Eat the :product!&quot;,<br>    &quot;product&quot;      =&gt; &quot;cheese&quot;<br>];</pre><blockquote>This file should be saved as <strong>app/lang/en/steps.php</strong>.</blockquote><pre>&lt;?php</pre><pre>return [<br>    &quot;instructions&quot; =&gt; &quot;Volg deze stappen om kaas te bedienen:&quot;,<br>    &quot;step1&quot;        =&gt; &quot;Snijd de kaas.&quot;,<br>    &quot;step2&quot;        =&gt; &quot;Eet de :product!&quot;,<br>    &quot;product&quot;      =&gt; &quot;kaas&quot;<br>];</pre><blockquote>This file should be saved as <strong>app/lang/nl/steps.php</strong>.</blockquote><pre>&lt;?php</pre><pre>class IndexController<br>extends BaseController<br>{<br>    public function indexAction()<br>    {<br>        App::setLocale(&quot;en&quot;);</pre><pre>        if (Input::get(&quot;lang&quot;) === &quot;nl&quot;)<br>        {<br>            App::setLocale(&quot;nl&quot;);<br>        }</pre><pre>        return View::make(&quot;index/index&quot;);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><p>This will toggle the language based on a querystring parameter. The next step is actually using the phrases in views:</p><pre><a href="http://twitter.com/extends">@extends</a>(&quot;layout&quot;)<br><a href="http://twitter.com/section">@section</a>(&quot;content&quot;)<br>    &lt;h1&gt;<br>        {{ Lang::get(&quot;steps.instructions&quot;) }}<br>    &lt;/h1&gt;<br>    &lt;ol&gt;<br>        &lt;li&gt;<br>            {{ trans(&quot;steps.step1&quot;) }}<br>        &lt;/li&gt;<br>        &lt;li&gt;<br>            {{ trans(&quot;steps.step2&quot;, [<br>                &quot;product&quot; =&gt; trans(&quot;steps.product&quot;)<br>            ]) }}<br>        &lt;/li&gt;<br>    &lt;/ol&gt;<br><a href="http://twitter.com/stop">@stop</a></pre><blockquote>This file should be saved as <strong>app/views/www/layout.blade.php</strong>.</blockquote><p>The <strong>Lang::get()</strong> method gets translated phrases out of the configuration files (in this case <strong>steps.instructions</strong>). The <strong>trans()</strong> method serves as a helpful alias to this.</p><p>You may also have noticed that <strong>step2</strong> has a strange placeholder (<strong>:product</strong>). This allows the insertion of variable data into translation phrases. We can pass these in the optional second parameter of <strong>Lang::get()</strong>/<strong>trans()</strong>.</p><blockquote>You can learn more about the Localization class at: <a href="http://laravel.com/docs/localization"><strong>http://laravel.com/<br>docs/localization</strong></a>.</blockquote><h4>Using Language Lookups in Packages</h4><p>We’re not going to go into the details of <a href="https://medium.com/on-coding/5963ca9d6499"><strong>How To Create Packages in Laravel 4</strong></a>, except to say that it’s possible to have package-specific translation. If you’ve set a package up, and registered its service provider in the application configuration, then you should be able to insert the following lines:</p><pre>public function boot()<br>{<br>    $this-&gt;package(&quot;formativ/multisite&quot;, &quot;multisite&quot;);<br>}</pre><blockquote>This was extracted from <strong>workbench/formativ/multisite/src/<br>Formativ/Multisite/MultisiteServiceProvider.php </strong>for brevity.</blockquote><p>…in the service provider. Your package will probably have a different vendor/package name, and you need to pay particular attention to the second parameter (which is the alias to your package assets).</p><p>Add these translation configuration files also:</p><pre>&lt;?php</pre><pre>return [<br>    &quot;instructions&quot; =&gt; &quot;Do these:&quot;<br>];</pre><blockquote>This file should be saved as <strong>workbench/formativ/multisite/src/<br>lang/en/steps.php</strong>.</blockquote><pre>&lt;?php</pre><pre>return [<br>    &quot;instructions&quot; =&gt; &quot;Hebben deze:&quot;<br>];</pre><blockquote>This file should be saved as <strong>workbench/formativ/multisite/src/<br>lang/nl/steps.php</strong>.</blockquote><p>Finally, let’s adjust the view to reflect the new translation phrase’s location:</p><pre>&lt;h1&gt;<br>    {{ Lang::get(&quot;multisite::steps.instructions&quot;) }}<br>&lt;/h1&gt;</pre><blockquote>This was extracted from <strong>app/views/www/index/index.blade.php </strong>for brevity.</blockquote><p>It’s as easy at that!</p><blockquote>You can learn more about package resources at: <a href="http://laravel.com/docs/packages#package-configuration"><strong>http://laravel.com/<br>docs/packages#package-configuration</strong></a>.</blockquote><h4>Caching Language Lookups</h4><p>Getting translated phrases from the filesystem can be an expensive operation, in a big system. What would be even cooler is if we could use Laravel 4&#39;s built-in cache system to make repeated lookups more efficient.</p><p>To do this, we need to create a few files, and change some old ones:</p><pre>// &#39;Lang&#39; =&gt; &#39;Illuminate\Support\Facades\Lang&#39;,<br>&quot;Lang&quot; =&gt; &quot;Formativ\Multisite\Facades\Lang&quot;,</pre><blockquote>This was extracted from <strong>app/config/app.php </strong>for brevity.</blockquote><p>This tells Laravel 4 to load our own Lang facade in place of the one that ships with Laravel 4. We’ve got to make this facade…</p><pre>&lt;?php</pre><pre>namespace Formativ\Multisite\Facades;</pre><pre>use Illuminate\Support\Facades\Facade;</pre><pre>class Lang<br>extends Facade<br>{<br>    protected static function getFacadeAccessor()<br>    {<br>        return &quot;multisite.translator&quot;;<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/multisite/src/<br>Formativ/Multisite/Facades/Lang.php</strong>.</blockquote><p>This is basically the same facade that ships with Laravel 4, but instead of returning <strong>translator</strong> it will return <strong>multisite.translator</strong>. We need to register this in our service provider as well:</p><pre>public function register()<br>{<br>    $this-&gt;app[&quot;multisite.translator&quot;] =<br>         $this-&gt;app-&gt;share(function($app)<br>    {<br>        $loader = $app[&quot;translation.loader&quot;];<br>        $locale = $app[&quot;config&quot;][&quot;app.locale&quot;];<br>        $trans  = new Translator($loader, $locale);</pre><pre>        return $trans;<br>    });<br>}</pre><blockquote>This was extracted from <strong>workbench/formativ/multisite/src/<br>Formativ/Multisite/MultisiteServiceProvider.php </strong>for brevity.</blockquote><p>I’ve used very similar code to what can be found in <strong>vendor/laravel/<br>framework/src/Illuminate/Translation/TranslationServiceProvider.php</strong>. That’s because I’m still using the old file-based loading system to get the translated phrases initially. We’ll only return cached data in subsequent retrievals.</p><p>Lastly; we need to override how the translator fetches the data.</p><pre>&lt;?php</pre><pre>namespace Formativ\Multisite;</pre><pre>use Cache;<br>use Illuminate\Translation\Translator as Original;</pre><pre>class Translator<br>extends Original<br>{<br>    public function get($key, array $replace = array(),<br>        $locale = null)<br>    {<br>        $cached = Cache::remember($key, 15,<br>            function() use ($key, $replace, $locale)<br>        {<br>            return parent::get($key, $replace, $local<br>        });</pre><pre>        return $cached;<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/multisite/src/<br>Formativ/Multisite/Translator.php</strong>.</blockquote><p>The process is as simple as subclassing the Translator class and caching the results of the first call to the <strong>get()</strong> method.</p><blockquote>You can learn more about facades at: <a href="http://laravel.com/docs/facades"><strong>http://laravel.com/docs/facades</strong></a>.</blockquote><h3>Creating Multi-Language Routes</h3><p>Making multi-language routes may seem needless, but link are important to search engines, and the users who have to remember them.</p><p>To make multi-language routes, we first need to create some link terms:</p><pre>&lt;?php</pre><pre>return [<br>    &quot;cheese&quot; =&gt; &quot;cheese&quot;,<br>    &quot;create&quot; =&gt; &quot;create&quot;,<br>    &quot;update&quot; =&gt; &quot;update&quot;,<br>    &quot;delete&quot; =&gt; &quot;delete&quot;<br>];</pre><blockquote>This file should be saved as <strong>app/lang/en/routes.php</strong>.</blockquote><pre>&lt;?php</pre><pre>return [<br>    &quot;cheese&quot; =&gt; &quot;kaas&quot;,<br>    &quot;create&quot; =&gt; &quot;creëren&quot;,<br>    &quot;update&quot; =&gt; &quot;bijwerken&quot;,<br>    &quot;delete&quot; =&gt; &quot;verwijderen&quot;<br>];</pre><blockquote>This file should be saved as <strong>app/lang/nl/routes.php</strong>.</blockquote><p>Next, we need to modify the <strong>app/routes.php</strong> file to dynamically create the routes:</p><pre>$locales = [<br>    &quot;en&quot;,<br>    &quot;nl&quot;<br>];</pre><pre>foreach ($locales as $locale)<br>{<br>    App::setLocale($locale);</pre><pre>    $cheese = trans(&quot;routes.cheese&quot;);<br>    $create = trans(&quot;routes.create&quot;);<br>    $update = trans(&quot;routes.update&quot;);<br>    $delete = trans(&quot;routes.delete&quot;);</pre><pre>    Route::any($cheese . &quot;/&quot; . $create,<br>        function() use ($cheese, $create)<br>    {<br>        return $cheese . &quot;/&quot; . $create;<br>    });</pre><pre>    Route::any($cheese . &quot;/&quot; . $update,<br>        function() use ($cheese, $update)<br>    {<br>        return $cheese . &quot;/&quot; . $update;<br>    });</pre><pre>    Route::any($cheese . &quot;/&quot; . $delete,<br>        function() use ($cheese, $delete)<br>    {<br>        return $cheese . &quot;/&quot; . $delete;<br>    });<br>}</pre><blockquote>This was extracted from <strong>app/routes.php </strong>for brevity.</blockquote><p>We hard-code the locale names because it’s the most efficient way to return them in the routes file. Routes are determined on each application request, so we dare not do a file lookup…</p><p>What this is basically doing is looping through the locales and creating routes based on translated phrases specific to the locale that’s been set. It’s a simple, but effective, mechanism for implementing multi-language routes.</p><p>If you would like to review the registered routes (for each language), you can run the following command:</p><pre>php artisan routes</pre><blockquote>You can learn more about routes at: <a href="http://laravel.com/docs/routing"><strong>http://laravel.com/docs/routing</strong></a>.</blockquote><h3>Creating Multi-Language Content</h3><p>Creating multi-language content is nothing more than having a few extra database table fields to hold the language-specific data. To do this; let’s make a migration and seeder to populate our database:</p><pre>&lt;?php</pre><pre>use Illuminate\Database\Migrations\Migration;</pre><pre>class CreatePostTable<br>extends Migration<br>{<br>    public function up()<br>    {<br>        Schema::create(&quot;post&quot;, function($table)<br>        {<br>            $table-&gt;increments(&quot;id&quot;);<br>            $table-&gt;string(&quot;title_en&quot;);<br>            $table-&gt;string(&quot;title_nl&quot;);<br>            $table-&gt;text(&quot;content_en&quot;);<br>            $table-&gt;text(&quot;content_nl&quot;);<br>            $table-&gt;timestamps();<br>        });<br>    }</pre><pre>    public function down()<br>    {<br>        Schema::dropIfExists(&quot;post&quot;);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/database/migrations/<br>0000_00_00_000000_CreatePostTable.php</strong>.</blockquote><pre>&lt;?php</pre><pre>class DatabaseSeeder<br>extends Seeder<br>{<br>    public function run()<br>    {<br>        Eloquent::unguard();<br>        $this-&gt;call(&quot;PostTableSeeder&quot;);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/database/seeds/DatabaseSeeder.php</strong>.</blockquote><pre>&lt;?php</pre><pre>class PostTableSeeder<br>extends DatabaseSeeder<br>{<br>    public function run()<br>    {<br>        $posts = [<br>            [<br>                &quot;title_en&quot;   =&gt; &quot;Cheese is the best&quot;,<br>                &quot;title_nl&quot;   =&gt; &quot;Kaas is de beste&quot;,<br>                &quot;content_en&quot; =&gt; &quot;Research has shown...&quot;,<br>                &quot;content_nl&quot; =&gt; &quot;Onderzoek heeft aangetoond...&quot;<br>            ]<br>        ];</pre><pre>        DB::table(&quot;post&quot;)-&gt;insert($posts);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/database/seeds/PostTableSeeder.php</strong>.</blockquote><p>To get all of this in the database, we need to check the settings in app/config/database.php and run the following command:</p><pre>php artisan migrate --seed --env=local</pre><p>This should create the post table and insert a single row into it. To access this table/row, we’ll make a model:</p><pre>&lt;?php</pre><pre>class Post<br>extends Eloquent<br>{<br>    protected $table = &quot;post&quot;;<br>}</pre><blockquote>This file should be saved as <strong>app/models/Post.php</strong>.</blockquote><p>We’ll not make a full set of views, but let’s look at what this data looks like straight out of the database. Update your <strong>IndexController</strong> to fetch the first post:</p><pre>&lt;?php</pre><pre>class IndexController<br>extends BaseController<br>{<br>    public function indexAction($sub)<br>    {<br>        App::setLocale($sub);</pre><pre>        return View::make(&quot;index/index&quot;, [<br>            &quot;post&quot; =&gt; Post::first()<br>        ]);<br>    }<br>}</pre><blockquote>This file should be saved as <strong>app/controllers/IndexController.php</strong>.</blockquote><p>Next, update the <strong>index/index.blade.php</strong> template:</p><pre>&lt;h1&gt;<br>    {{ $post-&gt;title_en }}<br>&lt;/h1&gt;<br>&lt;p&gt;<br>    {{ $post-&gt;content_en }}<br>&lt;/p&gt;</pre><blockquote>This was extracted from <strong>app/views/www/index/index.blade.php </strong>for brevity.</blockquote><p>If you managed to successfully run the migrations, have confirmed you have at least one table/row in the database, and made these changes; then you should be seeing the english post content in your <strong>index/index</strong> view.</p><p>That’s fine for the English site, but what about the other languages? We don’t want to have to add additional logic to determine which fields to show. We can’t use the translation layer for this either, because the translated phrases are in the database.</p><p>The answer is to modify the <strong>Post</strong> model:</p><pre>public function getTitleAttribute()<br>{<br>    $locale = App::getLocale();<br>    $column = &quot;title_&quot; . $locale;<br>    return $this-&gt;{$column};<br>}</pre><pre>public function getContentAttribute()<br>{<br>    $locale = App::getLocale();<br>    $column = &quot;content_&quot; . $locale;<br>    return $this-&gt;{$column};<br>}</pre><blockquote>This was extracted from <strong>app/models/Post.php </strong>for brevity.</blockquote><p><a href="https://medium.com/on-coding/c643022433ad"><strong>We’ve seen these attribute accessors before</strong></a>. They allow us to intercept calls to <strong>$post-&gt;title</strong> and <strong>$post-&gt;content</strong>, and provide our own return values. In this case; we return the locale-specific field value. Naturally we can adjust the view use:</p><pre>&lt;h1&gt;<br>    {{ $post-&gt;title }}<br>&lt;/h1&gt;<br>&lt;p&gt;<br>    {{ $post-&gt;content }}<br>&lt;/p&gt;</pre><blockquote>This was extracted from <strong>app/views/www/index/index.blade.php </strong>for brevity.</blockquote><p>We can use this in all the domain-specific views, to render locale-specific database data.</p><h3>Conclusion</h3><p>Laravel 4 is packed with excellent tools to build massive, multi-language, multi-domain applications. In addition, it also has an excellent ORM, template language, input validator and filter system. And that’s just the tip of the iceberg!</p><p>If you found this tutorial helpful, please tell me about it <a href="http://twitter.com/followchrisp">@followchrisp</a> and be sure to recommend it to PHP developers looking to Laravel 4!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/150/1*05D0E3dhjsTkABDRORHt0w.png" /></figure><p>This tutorial <a href="https://leanpub.com/laravel4cookbook">comes from a book I’m writing</a>. If you like it and want to support future tutorials; please consider buying it. Half of all sales go to Laravel.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=26cdc75e4810" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/laravel-4-multisites-26cdc75e4810">Laravel 4 Multisites</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 4 Packages]]></title>
            <link>https://medium.com/laravel-4/laravel-4-packages-5963ca9d6499?source=rss----ec6a040a914---4</link>
            <guid isPermaLink="false">https://medium.com/p/5963ca9d6499</guid>
            <dc:creator><![CDATA[Christopher Pitt]]></dc:creator>
            <pubDate>Tue, 07 Jan 2014 09:01:43 GMT</pubDate>
            <atom:updated>2014-01-07T08:31:10.211Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpFQOi1XHSUQmhjqbU2qXw.png" /></figure><h4>A Comprehensive Tutorial</h4><p>Laravel 4 is a huge step forward for the PHP community. It’s beautifully written, full of features and the community is presently exploding. Let’s contribute to that community by creating reusable packages, with Laravel.</p><blockquote><em>I have spent two full hours getting the code out of a Markdown document and into Medium. Medium really isn’t designed for tutorials such as this, and while much effort has been spent in the pursuit of accuracy; there’s a good chance you could stumble across a curly quote in a code listing. Please make a note and I will fix where needed.</em></blockquote><blockquote><em>I have also uploaded this code to Github. You need simply follow the configuration instructions in this tutorial, after downloading the source code, and the application should run fine. This assumes, of course, that you know how to do that sort of thing. If not; this shouldn’t be the first place you learn about making PHP applications.</em></blockquote><blockquote><a href="https://github.com/formativ/tutorial-laravel-4-packages"><strong>https://github.com/formativ/tutorial-laravel-4-packages</strong></a></blockquote><blockquote><em>If you spot differences between this tutorial and that source code, please raise it here or as a GitHub issue. Your help is greatly appreciated.</em></blockquote><p>This tutorial follows on from <a href="https://medium.com/on-coding/3bed5d0e645e">a previous tutorial</a> which covered the basics of creating a deployment process for your applications. It goes into detail about creating a Laravel 4 installation; so you should be familiar with it before spending a great deal of time in this one. You’ll also find the source-code we created there to be a good basis for understanding the source code of this tutorial.</p><p>I learned the really technical bits of this tutorial from reading <a href="https://leanpub.com/laravel">Taylor’s book</a>. If you take away just one thing from this it should be to get and read it.</p><h3>Composer</h3><p>While the previous tutorial shows you how to create a Laravel 4 project; we need to do a bit more work in this area if we are to understand one of the fundamental ways in which packages differ from application code.</p><p>Laravel 3 had the concept of bundles; which were downloadable folders of source code. Laravel 4 extends this idea, but instead of rolling its own download process, it makes use of Composer. Composer should not only be used to create new Laravel 4 installations; but also as a means of adding packages to existing Laravel 4 applications.</p><p>Laravel 4 packages are essentially the same things as normal Composer libraries. They often utilise functionality built into the Laravel 4 framework, but this isn’t a requirement of packages in general.</p><p>It follows that the end result of our work today is a stand-alone Composer library including the various deployment tools we created last time. For now, we will be placing the package code inside our application folder to speed up iteration time.</p><h3>Dependency Injection</h3><p>One of the darlings of modern PHP development is Dependency Injection. PHP developers have recently grown fond of unit testing and class decoupling. I’ll explain both of these things shortly; but it’s important to know that they are greatly improved by the use of dependency injection.</p><p>Let’s look at some non-injected dependencies, and the troubles they create for us.</p><pre>&lt;?php</pre><pre>class Archiver<br>{<br>    protected $database;</pre><pre>    public function __construct()<br>    {<br>        $this-&gt;database = Database::connect(<br>            &quot;host&quot;,<br>            &quot;username&quot;,<br>            &quot;password&quot;,<br>            &quot;schema&quot;<br>        );<br>    }</pre><pre>    public function archive()<br>    {<br>        $entries = $this-&gt;database-&gt;getEntries();</pre><pre>        foreach ($entries as $entry)<br>        {<br>            if ($entry-&gt;aggregated)<br>            {<br>                $entry-&gt;archive();<br>            }<br>        }<br>    }<br>}</pre><pre>$archiver = new Archiver();<br>$archiver-&gt;archive();</pre><p>Assuming, for a moment, that <strong>Database</strong> has been defined to pull all entries in such a way that they have an aggregated property and an <strong>archive()</strong> method; this code should be straightforward to follow.</p><p>The <strong>Archiver</strong> constructor initialises a database connection and stores a reference to it within a protected property. The <strong>archive()</strong> method iterates over the entries and archives them if they are already aggregated. Easy stuff.</p><p>Not so easy to test. The problem is that testing the business logic means testing the database connection and the entry instances as well. They are dependencies to any unit test for this class.</p><p>Furthermore; the Archiver class needs to know that these things exist and how they work. It’s not enough just to know that the database instance is available or that the <strong>getEntries()</strong> method returns entries. If we have thirty classes all depending on the database; something small (like a change to the database password) becomes a nightmare.</p><p>Dependency injection takes two steps, in PHP. The first is to declare dependencies outside of classes and pass them in:</p><pre>&lt;?php</pre><pre>class Archiver<br>{<br>    protected $database;</pre><pre>    public function __construct(Database $database)<br>    {<br>        $this-&gt;database = $database;<br>    }</pre><pre>    // ...then the archive() method<br>}</pre><pre>$database = Database::connect(<br>    &quot;host&quot;,<br>    &quot;username&quot;,<br>    &quot;password&quot;,<br>    &quot;schema&quot;<br>);</pre><pre>$archiver = new Archiver($database);<br>$archiver-&gt;archive();</pre><p>This may seem like a small, tedious change but it immediately enables independent unit testing of the database and the <strong>Archiver</strong> class.</p><p>The second step to proper dependency injection is to abstract the dependencies in such a way that they provide a minimum of functionality. Another way of looking at it is that we want to reduce the impact of changes or the leaking of details to classes with dependencies:</p><pre>&lt;?php</pre><pre>interface EntryProviderInterface<br>{<br>    public function getEntries();<br>}</pre><pre>class EntryProvider implements EntryProviderInterface<br>{<br>    protected $database;</pre><pre>    public function __construct(Database $database)<br>    {<br>        $this-&gt;database = $database;<br>    }</pre><pre>    public function getEntries()<br>    {<br>        // ...get the entries from the database<br>    }<br>}</pre><pre>class Archiver<br>{<br>    protected $provider;</pre><pre>    public function __construct(EntryProviderInterface $provider)<br>    {<br>        $this-&gt;provider = $provider;<br>    }</pre><pre>    // ...then the archive() method<br>}</pre><pre>$database = Database::connect(<br>    &quot;host&quot;,<br>    &quot;username&quot;,<br>    &quot;password&quot;,<br>    &quot;schema&quot;<br>);</pre><pre>$provider = new EntryProvider($database);</pre><pre>$archiver = new Archiver($provider);<br>$archiver-&gt;archive();</pre><p>Woah Nelly! That’s a lot of code when compared to the first snippet; but it’s worth it. Firstly; we’re exposing so few details to the <strong>Archiver</strong> class that the entire entry dependency could be replaced in a heartbeat. This is possible because we’ve moved the database dependency out of the Archiver and into the <strong>EntryProvider</strong>.</p><p>We’re also type-hinting an interface instead of a concrete class; which lets us swap the concrete class out with anything else that implements EntryProviderInterface. The concrete class can fetch the entries from the database, or the filesystem or whatever.</p><p>We can test the <strong>EntryProvider</strong> class by swapping in a fake <strong>Database</strong> instance. We can test the <strong>Archiver</strong> class by swapping in a fake <strong>EntryProvider</strong> instance.</p><p>So, to recap the requirements for dependency injection:</p><ol><li>Don’t create class instances in other classes (if they are dependencies) — pass them into the class from outside.</li><li>Don’t type-hint concrete classes — create interfaces.</li></ol><blockquote>“When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.</blockquote><blockquote>What you should be doing is stating a need, ‘I need something to drink with lunch,’ and then we will make sure you have something when you sit down to eat.<em>” — John Munsch</em></blockquote><blockquote>You can learn more about Dependency Injection, from Taylor’s book, at: <a href="https://leanpub.com/laravel"><strong>https://leanpub.com/laravel</strong></a>.</blockquote><h3>Inversion Of Control</h3><p>Inversion of Control (IoC) is the name given to the process that involves assembling class instances (and the resolution of class instances in general) within a container or registry. Where the registry pattern involves defining class instances and then storing them in some global container; IoC involves telling the container how and where to find the instances so that they can be resolved as required.</p><p>This naturally aids dependency injection as class instances adhering to abstracted requirements can easily be resolved without first creating. To put it another way; if our classes adhere to certain requirements (via interfaces) and the IoC container can resolve them to class instances, then we don’t have to do it beforehand.</p><p>The easiest way to understand this is to look at some code:</p><pre>&lt;?php</pre><pre>interface DatabaseInterface<br>{<br>    // ...<br>}</pre><pre>class Database implements DatabaseInterface<br>{<br>    // ...<br>}</pre><pre><br>interface EntryProviderInterface<br>{<br>    // ...<br>}</pre><pre>class EntryProvider implements EntryProviderInterface<br>{<br>    protected $database;</pre><pre>    public function __construct(DatabaseInterface $database)<br>    {<br>        $this-&gt;database = $database;<br>    }</pre><pre>    // ...<br>}</pre><pre>interface ArchiverInterface<br>{<br>    // ...<br>}</pre><pre>class Archiver implements ArchiverInterface<br>{<br>    protected $provider;</pre><pre>    public function __construct(EntryProviderInterface $provider)<br>    {<br>        $this-&gt;provider = $provider;<br>    }</pre><pre>    // ...<br>}</pre><pre>$database = new Database();<br>$provider = new EntryProvider($database);<br>$archiver = new Archiver($provider);</pre><p>This shallowly represents a dependency (injection) chain. The last three lines are where the problem starts to become clear; the more we abstract our dependencies, the more “bootstrapping” code needs to be done every time we need the <strong>Archiver</strong> class.</p><p>We could abstract this by using Laravel 4&#39;s IoC container:</p><pre>&lt;?php</pre><pre>// ...define classes</pre><pre>App::bind(&quot;DatabaseInterface&quot;, function() {<br>    return new Database();<br>});</pre><pre>App::bind(&quot;EntryProviderInterface&quot;, function() {<br>    return new EntryProvider(App::make(&quot;DatabaseInterface&quot;));<br>});</pre><pre>App::bind(&quot;ArchiverInterface&quot;, function() {<br>    return new Archiver(App::make(&quot;EntryProviderInterface&quot;));<br>});</pre><pre>$archiver = App::make(&quot;ArchiverInterface&quot;);</pre><p>These extra nine lines (using the <strong>App::bind()</strong> and <strong>App::make() </strong>methods) tell Laravel 4 how to find/make new class instances, so we can get to the business of using them!</p><blockquote>You can learn more about IoC container at: <a href="http://laravel.com/docs/ioc"><strong>http://laravel.com/docs/ioc</strong></a>.</blockquote><h3>Service Providers</h3><p>The main purpose of services providers is to collect and organise the bootstrapping requirements of your package. They’re not strictly a requirement of package development; but rather a Really Good Idea™.</p><p>There are three big things to service providers. The first is that they are registered in a common configuration array (in <strong>app/config/app.php</strong>):</p><pre>&#39;providers&#39; =&gt; array(</pre><pre> &#39;Illuminate\Foundation\Providers\ArtisanServiceProvider&#39;,<br> &#39;Illuminate\Auth\AuthServiceProvider&#39;,<br> &#39;Illuminate\Cache\CacheServiceProvider&#39;,<br> // ...<br> &#39;Illuminate\Validation\ValidationServiceProvider&#39;,<br> &#39;Illuminate\View\ViewServiceProvider&#39;,<br> &#39;Illuminate\Workbench\WorkbenchServiceProvider&#39;,</pre><pre>),</pre><blockquote>This was extracted from <strong>app/config/app.php </strong>for brevity.</blockquote><p>You can also add your own service providers to this list; as many packages will recommend you do. Then there’s the <strong>register()</strong> method:</p><pre>&lt;?php namespace Illuminate\Cookie;</pre><pre>use Illuminate\Support\ServiceProvider;</pre><pre>class CookieServiceProvider extends ServiceProvider {</pre><pre>    /**<br>    * Register the service provider.<br>    *<br>    * @return void<br>    */<br>    public function register()<br>    {<br>        $this-&gt;app[&#39;cookie&#39;] = $this-&gt;app-&gt;share(function($app)<br>        {<br>            $cookies = new CookieJar($app[&#39;request&#39;], $app[&#39;encrypter&#39;]);</pre><pre>            $config = $app[&#39;config&#39;][&#39;session&#39;];</pre><pre>            return $cookies-&gt;setDefaultPathAndDomain($config[&#39;path&#39;], $config[&#39;domain&#39;]);<br>        });<br>    }</pre><pre>}</pre><blockquote>This file should be saved as <strong>vendor/laravel/framework/src/Illuminate/<br>Cookie/CookieServiceProvider.php</strong>.</blockquote><p>When we take a look at the <strong>register()</strong> method of the <strong>CookieServiceProvider</strong> class; we can see a call to <strong>$this-&gt;app-&gt;share()</strong> which is similar to the<strong>App::bind()</strong> method but instead of creating a new class instance every time it’s resolved in the IoC container; <strong>share()</strong> wraps a callback so that it’s shared with every resolution.</p><p>The name of the <strong>register()</strong> method explains exactly what it should be used for; registering things with the IoC container (which <strong>App</strong> extends). If you need to do other bootstrapping stuff then the method you need to use is <strong>boot()</strong>:</p><pre>public function boot()<br>{<br>    Model::setConnectionResolver($this-&gt;app[&#39;db&#39;]);</pre><pre>    Model::setEventDispatcher($this-&gt;app[&#39;events&#39;]);<br>}</pre><blockquote>This was extracted from <strong>vendor/laravel/framework/src/Illuminate/<br>Database/DatabaseServiceProvider.php </strong>for brevity.</blockquote><p>This <strong>boot()</strong> method sets two properties on the <strong>Model</strong> class. The same class also has a register method but these two settings are pet for the boot method.</p><blockquote>You can learn more about service providers at: <a href="http://laravel.com/docs/packages#service-providers"><strong>http://laravel.com/docs/<br>packages#service-providers</strong></a></blockquote><h3>Organising Code</h3><p>Now that we have some tools to help us create packages; we need to port our code over from being application code to being a package. Laravel 4 provides a useful method for creating some structure to our package:</p><pre>php artisan workbench Formativ/Deployment</pre><p>You’ll notice a new folder has been created, called workbench. Within this folder you’ll find an assortment of files arranged in a similar way to those in the <strong>vendor/laravel/framework/src/Illuminate/*</strong> folders.</p><p>We need to break all of the business logic (regarding deployment) into individual classes, making use of the IoC container and dependency injection, and make a public API.</p><blockquote>We’re not going to spend a lot of time discussing the intricacies of the deployment classes/scripts we made in the last tutorial. Refer to it if you want more of those details.</blockquote><p>To recap; the commands we need to abstract are:</p><ul><li>asset:combine</li><li>asset:minify</li><li>clean</li><li>copy</li><li>distribute</li><li>environment:get</li><li>environment:remove</li><li>environment:set</li><li>watch</li></ul><p>Many of these were cluttering up the list of commands which ship with Laravel. Our organisation should collect all of these in a common “namespace”.</p><p>The first step is to create a few contracts (interfaces) for the API we want to expose through our package:</p><pre>&lt;?php</pre><pre>namespace Formativ\Deployment;</pre><pre>interface DistributionInterface<br>{<br>    public function prepare($source, $target);<br>    public function sync();<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/DistributionInterface.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment;</pre><pre>interface MachineInterface<br>{<br>    public function getEnvironment();<br>    public function setEnvironment($environment);<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/MachineInterface.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Asset;</pre><pre>interface ManagerInterface<br>{<br>    public function add($source, $target);<br>    public function remove($target);<br>    public function combine();<br>    public function minify();<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Asset/ManagerInterface.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Asset;</pre><pre>interface WatcherInterface<br>{<br>    public function watch();<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Asset/WatcherInterface.php</strong>.</blockquote><p>The methods required by these interfaces encompass the functionality our previous commands provided for us. The next step is to fulfil these contracts with concrete class implementations:</p><pre>&lt;?php</pre><pre>namespace Formativ\Deployment;</pre><pre>use Formativ\Deployment\MachineInterface;<br>use Illuminate\Filesystem\Filesystem;</pre><pre>class Distribution implements DistributionInterface<br>{<br>    protected $files;</pre><pre>    protected $machine;</pre><pre>    public function __construct(<br>        Filesystem $files,<br>        MachineInterface $machine<br>    )<br>    {<br>        $this-&gt;files = $files;<br>        $this-&gt;machine = $machine;<br>    }</pre><pre>    public function prepare($source, $target)<br>    {<br>        // ...copy + clean files for distribution<br>    }</pre><pre>    public function sync()<br>    {<br>        // ...sync distribution files to remote server<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Distribution.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment;</pre><pre>use Illuminate\Filesystem\Filesystem;</pre><pre>class Machine implements MachineInterface<br>{<br>    protected $environment;</pre><pre>    protected $files;</pre><pre>    public function __construct(Filesystem $files)<br>    {<br>        $this-&gt;files = $files;<br>    }</pre><pre>    public function getEnvironment()<br>    {<br>        // ...get the current environment<br>    }</pre><pre>    public function setEnvironment($environment)<br>    {<br>        // ...set the current environment<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Machine.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Asset;</pre><pre>use Formativ\Deployment\MachineInterface;<br>use Illuminate\Filesystem\Filesystem;</pre><pre>class Manager implements ManagerInterface<br>{<br>    protected $files;</pre><pre>    protected $machine;</pre><pre>    protected $assets = [];</pre><pre>    public function __construct(<br>        Filesystem $files,<br>        MachineInterface $machine<br>    )<br>    {<br>        $this-&gt;files = $files;<br>        $this-&gt;machine = $machine;<br>    }   </pre><pre>    public function add($source, $target)<br>    {<br>        // ...add files to $assets<br>    }</pre><pre>    public function remove($target)<br>    {<br>        // ...remove files from $assets<br>    }</pre><pre>    public function combine()<br>    {<br>        // ...combine assets<br>    }</pre><pre>    public function minify()<br>    {<br>        // ...minify assets<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Asset/Manager.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Asset;</pre><pre>use Formativ\Deployment\MachineInterface;<br>use Illuminate\Filesystem\Filesystem;</pre><pre>class Watcher implements WatcherInterface<br>{<br>    protected $files;</pre><pre>    protected $machine;</pre><pre>    public function __construct(<br>        Filesystem $files,<br>        MachineInterface  $machine<br>    )<br>    {<br>        $this-&gt;files = $files;<br>        $this-&gt;machine = $machine;<br>    }</pre><pre>    public function watch()<br>    {<br>        // ...watch assets for changes<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Asset/Watcher.php</strong>.</blockquote><p>The main to note about these implementations is that they use dependency injection instead of creating class instances in their constructors. As I pointed out before; we’re not going to go over the actually implementation code, but feel free to port it over from the application-based commands.</p><p>As you can probably tell; I’ve combined related functionality into four main classes. This becomes even more apparent when you consider what the service provider has become:</p><pre>&lt;?php</pre><pre>namespace Formativ\Deployment;</pre><pre>use App;<br>use Illuminate\Support\ServiceProvider;</pre><pre>class DeploymentServiceProvider<br>extends ServiceProvider<br>{<br>    protected $defer = true;</pre><pre>    public function register()<br>    {<br>        App::bind(&quot;deployment.asset.manager&quot;, function()<br>        {<br>            return new Asset\Manager(<br>                App::make(&quot;files&quot;),<br>                App::make(&quot;deployment.machine&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.asset.watcher&quot;, function()<br>        {<br>            return new Asset\Watcher(<br>                App::make(&quot;files&quot;),<br>                App::make(&quot;deployment.machine&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.distribution&quot;, function()<br>        {<br>            return new Distribution(<br>                App::make(&quot;files&quot;),<br>                App::make(&quot;deployment.machine&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.machine&quot;, function()<br>        {<br>            return new Machine(<br>                App::make(&quot;files&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.asset.combine&quot;, function()<br>        {<br>            return new Command\Asset\Combine(<br>                App::make(&quot;deployment.asset.manager&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.asset.minify&quot;, function()<br>        {<br>            return new Command\Asset\Minify(<br>                App::make(&quot;deployment.asset.manager&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.asset.watch&quot;, function()<br>        {<br>            return new Command\Asset\Watch(<br>                App::make(&quot;deployment.asset.manager&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.distribute.prepare&quot;, function()<br>        {<br>            return new Command\Distribute\Prepare(<br>                App::make(&quot;deployment.distribution&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.distribute.sync&quot;, function()<br>        {<br>            return new Command\Distribute\Sync(<br>                App::make(&quot;deployment.distribution&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.machine.add&quot;, function()<br>        {<br>            return new Command\Machine\Add(<br>                App::make(&quot;deployment.machine&quot;)<br>            );<br>        });</pre><pre>        App::bind(&quot;deployment.command.machine.remove&quot;, function()<br>        {<br>            return new Command\Machine\Remove(<br>                App::make(&quot;deployment.machine&quot;)<br>            );<br>        });</pre><pre>        $this-&gt;commands(<br>            &quot;deployment.command.asset.combine&quot;,<br>            &quot;deployment.command.asset.minify&quot;,<br>            &quot;deployment.command.asset.watch&quot;,<br>            &quot;deployment.command.distribute.prepare&quot;,<br>            &quot;deployment.command.distribute.sync&quot;,<br>            &quot;deployment.command.machine.add&quot;,<br>            &quot;deployment.command.machine.remove&quot;<br>        );<br>    }</pre><pre>    public function boot()<br>    {<br>        $this-&gt;package(&quot;formativ/deployment&quot;);</pre><pre>        include __DIR__ . &quot;/../../helpers.php&quot;;<br>        include __DIR__ . &quot;/../../macros.php&quot;;<br>    }</pre><pre>    public function provides()<br>    {<br>        return [<br>            &quot;deployment.asset.manager&quot;,<br>            &quot;deployment.asset.watcher&quot;,<br>            &quot;deployment.distribution&quot;,<br>            &quot;deployment.machine&quot;,<br>            &quot;deployment.command.asset.combine&quot;,<br>            &quot;deployment.command.asset.minify&quot;,<br>            &quot;deployment.command.asset.watch&quot;,<br>            &quot;deployment.command.distribute.prepare&quot;,<br>            &quot;deployment.command.distribute.sync&quot;,<br>            &quot;deployment.command.machine.add&quot;,<br>            &quot;deployment.command.machine.remove&quot;<br>       ];<br>    }<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/DeploymentServiceProvider.php</strong>.</blockquote><blockquote>I’ve folded the <strong>copy()</strong> and <strong>clean()</strong> methods into a single <strong>prepare()</strong> method.</blockquote><p>The first four <strong>bind()</strong> calls bind our four main classes to the IoC container. The remaining <strong>bind()</strong> methods bind the artisan commands we still have to create (to replace those we make last time).</p><p>There’s also a call to <strong>$this-&gt;commands()</strong>; which registers commands (bound to the IoC container) with artisan. Finally; we define the <strong>provides()</strong> method, which coincides with the <strong>$defer = true</strong> property, to inform Laravel which IoC container bindings are returned by this service provider. By setting <strong>$defer = true</strong>; we’re instructing Laravel to not immediately load the provided classes and commands, but rather wait until they are required.</p><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Asset;</pre><pre>use Formativ\Deployment\Asset\ManagerInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Combine<br>extends Command<br>{<br>    protected $name = &quot;deployment:asset:combine&quot;;</pre><pre>    protected $description = &quot;Combines multiple resource files.&quot;;</pre><pre>    public function __construct(ManagerInterface $manager)<br>    {<br>        parent::__construct();<br>        $this-&gt;manager = $manager;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Asset/Combine.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Asset;</pre><pre>use Formativ\Deployment\Asset\ManagerInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Minify<br>extends Command<br>{<br>    protected $name = &quot;deployment:asset:minify&quot;;</pre><pre>    protected $description = &quot;Minifies multiple resource files.&quot;;</pre><pre>    public function __construct(ManagerInterface $manager)<br>    {<br>        parent::__construct();<br>        $this-&gt;manager = $manager;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Asset/Minify.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Asset;</pre><pre>use Formativ\Deployment\Asset\ManagerInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Watch<br>extends Command<br>{<br>    protected $name = &quot;deployment:asset:watch&quot;;</pre><pre>    protected $description = &quot;Watches files for changes.&quot;;</pre><pre>    public function __construct(ManagerInterface $manager)<br>    {<br>       parent::__construct();<br>       $this-&gt;manager = $manager;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Asset/Watch.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Distribute;</pre><pre>use Formativ\Deployment\DistributionInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Prepare<br>extends Command<br>{<br>    protected $name = &quot;deployment:distribute:prepare&quot;;</pre><pre>    protected $description = &quot;Prepares the distribution folder.&quot;;</pre><pre>    protected $distribution;</pre><pre>    public function __construct(<br>        DistributionInterface $distribution<br>    )<br>    {<br>        parent::__construct();<br>        $this-&gt;distribution = $distribution;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Distribute/Prepare.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Distribute;</pre><pre>use Formativ\Deployment\DistributionInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Sync<br>extends Command<br>{<br>    protected $name = &quot;deployment:distribute:sync&quot;;</pre><pre>    protected $description = &quot;Syncs changes to a target.&quot;;</pre><pre>    protected $distribution;</pre><pre>    public function __construct(<br>        DistributionInterface $distribution<br>    )<br>    {<br>        parent::__construct();<br>        $this-&gt;distribution = $distribution;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Distribute/Sync.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Machine;</pre><pre>use Formativ\Deployment\MachineInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Add<br>extends Command<br>{<br>    protected $name = &quot;deployment:machine:add&quot;;</pre><pre>    protected $description = &quot;Adds the current machine to an environment.&quot;;</pre><pre>    protected $machine;</pre><pre>    public function __construct(MachineInterface $machine)<br>    {<br>        parent::__construct();<br>        $this-&gt;machine = $machine;<br>    }</pre><pre>    // ...<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Machine/Add.php</strong>.</blockquote><pre>&lt;?php</pre><pre>namespace Formativ\Deployment\Command\Machine;</pre><pre>use Formativ\Deployment\MachineInterface;<br>use Illuminate\Console\Command;<br>use Symfony\Component\Console\Input\InputArgument;<br>use Symfony\Component\Console\Input\InputOption;</pre><pre>class Remove<br>extends Command<br>{<br>    protected $name = &quot;deployment:machine:remove&quot;;</pre><pre>    protected $description = &quot;Removes the current machine from an environment.&quot;;</pre><pre>    protected $machine;</pre><pre>    public function __construct(MachineInterface $machine)<br>    {<br>        parent::__construct();<br>        $this-&gt;machine = $machine;<br>    }</pre><pre>    // ...<br>}</pre><blockquote><em>This file should be saved as </em><strong><em>workbench/formativ/deployment/src/<br>Formativ/Deployment/Command/Machine/Remove.php</em></strong><em>.</em></blockquote><blockquote>I’ve also omitted the <strong>fire()</strong> method from the new commands; consider it an exercise to add these in yourself.</blockquote><p>Now, when we run the artisan list command; we should see all of our new package commands neatly grouped. Feel free to remove the old commands, after you’ve ported their functionality over to the new ones.</p><h3>Publishing Configuration Files</h3><p>Often you’ll want to add new configuration files to the collection which ship with new Laravel applications. There’s an artisan command to help with this:</p><pre>php artisan config:publish formativ/deployment --path=<br>workbench/Formativ/Deployment/src/config</pre><p>This should copy the package configuration files into the <strong>app/config/packages/formativ/deployment</strong> directory.</p><blockquote>Since we have access to a whole new kind of configuration; we no longer need to override the configuration files for things like environment. As long as our helpers/macros use the package configuration files instead of the the default ones; we can leave the underlying Laravel 4 application structure (and bootstrapping code) untouched.</blockquote><h3>Creating Composer.json</h3><p>Before we can publish our package, so that others can use it in their applications; we need to add a few things to the <strong>composer.json</strong> file that the workbench command created for us:</p><pre>{<br>    &quot;name&quot;        : &quot;formativ/deployment&quot;,<br>    &quot;description&quot; : &quot;All sorts of cool things with deployment.&quot;,<br>    &quot;authors&quot;     : [<br>        {<br>            &quot;name&quot;  : &quot;Christopher Pitt&quot;,<br>            &quot;email&quot; : &quot;cgpitt@gmail.com&quot;<br>        }<br>    ],<br>    &quot;require&quot; : {<br>        &quot;php&quot;                : &quot;&gt;=5.4.0&quot;,<br>        &quot;illuminate/support&quot; : &quot;4.0.x&quot;<br>    },<br>    &quot;autoload&quot; : {<br>        &quot;psr-0&quot; : {<br>            &quot;Formativ\\Deployment&quot; : &quot;src/&quot;<br>        }<br>    },<br>    &quot;minimum-stability&quot; : &quot;dev&quot;<br>}</pre><blockquote>This file should be saved as <strong>workbench/formativ/deployment/<br>composer.json</strong>.</blockquote><p>I’ve added a description, my name and email address. I also cleaned up the formatting a bit; but that’s not really a requirement. I’ve also set the minimum PHP version to 5.4.0 as we’re using things like short array syntax in our package.</p><blockquote>You should also create a <strong>readme.md</strong> file, which contains installation instructions, usage instructions, license and contribution information. I can’t understate the import ants of these things — they are your only hope to attract contributors.</blockquote><blockquote>You can learn more about the <strong>composer.json</strong> file at: <a href="http://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup"><strong>http://getcomposer.org/doc/01-basic-usage.md<br>#composer-json-project-setup</strong></a>.</blockquote><h3>Submitting A Package To Packagist</h3><p>To get others using your packages; you need to submit them to packagist.org. Go to <a href="http://packagist.org/"><strong>http://packagist.org</strong></a> and sign in. If you haven’t already got an account, you should create it and link to your GitHub account.</p><p>You’ll find a big “Submit Package” button on the home page. Click it and paste the URL to your GitHub repository in the text field. Submit the form and you should be good to go.</p><blockquote>You can learn more about package development at: <a href="http://laravel.com/docs/packages"><strong>http://laravel.com/<br>docs/packages</strong></a>.</blockquote><h3>Note On Testing</h3><p>Those of you who already know about unit testing will doubtlessly wonder where the section on testing is. I ran short on time for that in this tutorial, but it will be the subject of a future tutorial; which will demonstrate the benefits of dependency injection as it relates to unit testing.</p><h3><strong>Conclusion</strong></h3><p>Laravel 4 is packed with excellent tools to enable secure deployment. In addition, it also has an excellent ORM, template language, input validator and filter system. And that’s just the tip of the iceberg!</p><p>If you found this tutorial helpful, please tell me about it <a href="https://twitter.com/followchrisp">@followchrisp</a> and be sure to recommend it to PHP developers looking to Laravel 4!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/150/1*05D0E3dhjsTkABDRORHt0w.png" /></figure><p>This tutorial comes <a href="https://leanpub.com/laravel4cookbook">from a book I’m writing</a>. If you like it and want to support future tutorials; please consider buying it. Half of all sales go to Laravel.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5963ca9d6499" width="1" height="1" alt=""><hr><p><a href="https://medium.com/laravel-4/laravel-4-packages-5963ca9d6499">Laravel 4 Packages</a> was originally published in <a href="https://medium.com/laravel-4">Laravel 4 Tutorials</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>