<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Djordje's Blog]]></title><description><![CDATA[My dev notes]]></description><link>https://blog.djordjekovacevic.com/</link><image><url>https://blog.djordjekovacevic.com/favicon.png</url><title>Djordje&apos;s Blog</title><link>https://blog.djordjekovacevic.com/</link></image><generator>Ghost 3.40</generator><lastBuildDate>Thu, 12 Mar 2026 13:39:28 GMT</lastBuildDate><atom:link href="https://blog.djordjekovacevic.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Using MinIO as a development S3 replacement]]></title><description><![CDATA[<p>There are parts of your stack that can be run locally and others that can't. If you're using AWS for your cloud infrastructure you most likely use S3 as an object storage. Tool named<a href="https://min.io/"> MinIO</a> provides an S3 compliant API that gives you ability to have an additional piece of</p>]]></description><link>https://blog.djordjekovacevic.com/articles/using-minio-as-a-development-s3-replacement/</link><guid isPermaLink="false">5ede8a45a84ba2000123a259</guid><category><![CDATA[Dev Tools]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Wed, 02 Sep 2020 21:01:23 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1584169417032-d34e8d805e8b?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1584169417032-d34e8d805e8b?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Using MinIO as a development S3 replacement"><p>There are parts of your stack that can be run locally and others that can't. If you're using AWS for your cloud infrastructure you most likely use S3 as an object storage. Tool named<a href="https://min.io/"> MinIO</a> provides an S3 compliant API that gives you ability to have an additional piece of puzzle running locally during development.</p><p>You can have your app running locally and still use a bunch of services in the cloud, so you ask why do you need it. The answer is simple, you can run a dev environment totally independent on your internet connection (eg. in the plane), you don't have additional costs, you can ship dev configuration in the source code, <strong>you can write tests that will create and destroy anything you need</strong>.</p><p>But not just that, MinIO is production ready software. It means you can run it on top of your infrastructure and still be able to use a lot of ready made libs available for S3.</p><p>Here's very simple docker compose configuration that can bootstrap you:</p><pre><code class="language-yaml">version: '3.5'

volumes:
  s3:

services:
  minio:
    image: minio/minio:latest
    volumes:
      - s3:/data
    ports:
      - 9000:9000
    environment:
      - "MINIO_ACCESS_KEY=test-s3-access-key"
      - "MINIO_SECRET_KEY=test-s3-secret-key"
    command: "server /data"

  createbuckets:
    image: minio/mc:latest
    depends_on:
      - minio
    entrypoint: &gt;
      sh -c '
      sleep 3 &amp;&amp;
      mc config host add s3 http://minio:9000 "test-s3-access-key" "test-s3-secret-key" &amp;&amp;
      mc mb -p s3/blog &amp;&amp;
      mc policy set download s3/blog &amp;&amp;
      exit 0
      '
</code></pre><p>It will start a MinIO server using the official Docker image and make it store files in the s3 volume (you can also use local directory if you want to inspect files manually). It will be available on port <code>9000</code> and you can use access and secret keys to access programmatically or via MinIO web UI.</p><p>MinIO provides only storage infrastructure and basic Web UI. To be able to use full potential of it you need tool shipped as a separate executable to configure it.<br>Tool is <code>mc</code> and you can use it to configure local or remote MinIO servers. In this example service named <code>createbuckerts</code> is responsible to create buckets we need and die. In the entrypoint sleep ensures we give some time to MinIO to start before we start configuring. Then we configure new host (in real life applications you can use <code>mc</code> to access multiple MinIO servers). You see that we're giving it URL (using Docker service name) and access and secret keys that have rights to change configuration.<br>Than we're making a bucket in <code>s3</code> host named <code>blog</code>.<br>Then we're setting policy to download to make it possible to read files without additional auth, basically public read. And finally exit shell script.</p><p>Using <code>mc</code> you can do a bunch of other things. Eg. create additional users or policies and assign different policies for different users.</p>]]></content:encoded></item><item><title><![CDATA[Restoring UniFi CK]]></title><description><![CDATA[Explanation how to restore UniFi Cloud Key (1st gen) after failing upgrade.
UniFi controller reinstallation or restoring backup on factory reset device.]]></description><link>https://blog.djordjekovacevic.com/articles/restoring-unifi-ck/</link><guid isPermaLink="false">5eb9cb1149a1a60001bf2eab</guid><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Sat, 11 Jan 2020 00:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1521106047354-5a5b85e819ee?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1521106047354-5a5b85e819ee?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Restoring UniFi CK"><p>I run a couple of Ubiquiti's devices in my home network. I host my controller on CloudKey (1st gen), that's <a href="https://www.ui.com/unifi/unifi-cloud-key/">this device</a>.</p>
<p>Equipment is pretty much rock solid, but I had two failing controller upgrades on CK in the last 6 months. That's a situation when blinking white light turns constant white and never gets to the constant blue.</p>
<hr>
<p>I read a few articles on this topic and ended up following two scenarios from the official documentation.</p>
<p>First of all I wasn't able to access cloudkey's web UI. Page where you can go to the controller of CK's configuration. So I used mostly SSH which should be straightforward.</p>
<p>I'd strongly suggest downloading backup .unf file before upgrading. If you have auto backup enabled you can get it from the filesystem, but it's simply easier to have backup before you end up in this situation.</p>
<h3 id="reinstallingcontroller">Reinstalling controller</h3>
<p>I'd try this as a first step as it should be quicker and easier to do.<br>
Here's a <a href="https://help.ubnt.com/hc/en-us/articles/216655518-UniFi-How-to-Change-the-Cloud-Key-s-Controller-Version-via-SSH" target="_blank">link</a> to official documentation.</p>
<p>Steps are:</p>
<ul>
<li>
<p>Go to <a href="https://www.ui.com/download/unifi/unifi-cloud-key" target="_blank">UniFI DL page</a></p>
</li>
<li>
<p>Find desired controller software for Debian/Ubuntu and UniFi CloudKey and copy DL link. I'll use this one in the example below:</p>
<pre><code class="language-sh">https://dl.ui.com/unifi/5.12.22/unifi_sysvinit_all.deb
</code></pre>
</li>
<li>
<p>SSH to your controller (keep in mind to use your CK configured username/password and IP)</p>
<pre><code class="language-sh">ssh djordje@10.10.1.100
</code></pre>
</li>
<li>
<p>From the controller execute this:</p>
<pre><code class="language-sh">cd /tmp
wget https://dl.ui.com/unifi/5.12.22/unifi_sysvinit_all.deb
dpkg -i unifi_sysvinit_all.deb
</code></pre>
</li>
<li>
<p>When controller is installed you can remove installer:</p>
<pre><code class="language-sh">rm /tmp/unifi_sysvinit_all.deb
</code></pre>
</li>
<li>
<p>Exit using <code>exit</code> command or <code>Ctrl + D</code></p>
</li>
<li>
<p>Wait for blue light to access your controller</p>
</li>
</ul>
<h3 id="resettingtofactorydefaultsandrestoringbackup">Resetting to factory defaults and restoring backup</h3>
<p>Unless the previous step helped you, you should try this one. It's based on official documentation available at <a href="https://help.ubnt.com/hc/en-us/articles/360000128688-UniFi-Troubleshooting-Offline-Cloud-Key-and-Other-Stability-Issues">this link</a>.</p>
<p>Before starting you should have a <code>.unf</code> backup file on your computer. Unless you have it you can extract it from CK using <code>scp</code>.</p>
<p>Obtaining backups:</p>
<ul>
<li>
<p>SSH to CK and check do you have backups:</p>
<pre><code class="language-sh">ssh djordje@10.10.1.100
ls -la /data/autobackup
exit
</code></pre>
</li>
<li>
<p>If you have at least one backup copy it to your computer:</p>
<pre><code class="language-sh">cd Desktop/ &amp;&amp; mkdir unf_bkp &amp;&amp; cd unf_bkp
scp djordje@10.10.1.100:/data/autobackup/*.* .
</code></pre>
</li>
</ul>
<p>Now you're ready to reset to factory defaults:</p>
<ul>
<li>
<p>Reset to default over SSH:</p>
<pre><code class="language-sh">ssh djordje@10.10.1.100
ubnt-systool reset2defaults
</code></pre>
</li>
<li>
<p>When everything is done you should be able to access the controller and go to configuration to setup your username/password again. Previous step restored default username/password <code>ubnt</code>.</p>
</li>
<li>
<p>From the config check again for firmware/controller updates and apply if needed.</p>
</li>
<li>
<p>When the controller is up go to web UI (same IP as CK, but port <code>8443</code>) and you should see configuration wizard.</p>
</li>
<li>
<p>Select to restore from backup and wait.</p>
</li>
<li>
<p>When previous step is done you should be able to login with your username/password combo and see whole site as nothing has happened 😊</p>
</li>
</ul>
<h3 id="additionalresources">Additional resources</h3>
<ul>
<li>I didn't have issues with mongoDB on my CK, but you may have it. Here's a <a href="https://help.ubnt.com/hc/en-us/articles/360006634094-UniFi-Network-Controller-Repairing-Database-Issues-on-the-UniFi-Controller#2">link</a> to official documentation.</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[My migration from MySQL to PostgreSQL]]></title><description><![CDATA[<p>I've been thinking about this, since I've done <a href="https://blog.djordjekovacevic.com/articles/php-cloud-hosting-comparison-(openshift-vs-heroku-vs-fortrabbit)">PHP cloud hosting comparison (OpenShift vs Heroku vs Fortrabbit)</a>. I decided to give it a chance on my blog, because DB is small, and I know all details about the project.</p><p>During the preparation I've read a few articles and discussions on</p>]]></description><link>https://blog.djordjekovacevic.com/articles/my-migration-from-mysql-to-postgresql/</link><guid isPermaLink="false">5eb9a8a249a1a60001bf2e71</guid><category><![CDATA[DB]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Tue, 15 Mar 2016 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>I've been thinking about this, since I've done <a href="https://blog.djordjekovacevic.com/articles/php-cloud-hosting-comparison-(openshift-vs-heroku-vs-fortrabbit)">PHP cloud hosting comparison (OpenShift vs Heroku vs Fortrabbit)</a>. I decided to give it a chance on my blog, because DB is small, and I know all details about the project.</p><p>During the preparation I've read a few articles and discussions on this topic. And I thought everything will be easy, but I ran into some minor issues.</p><h3 id="frommysqltopostgresql">FromMySqlToPostgreSql</h3><p>I've started at <a href="https://wiki.postgresql.org/wiki/Converting_from_other_Databases_to_PostgreSQL">Converting from other Databases to PostgreSQL</a> and found out about <a href="https://github.com/AnatolyUss/FromMySqlToPostgreSql">AnatolyUss/FromMySqlToPostgreSql</a>. I was surprised how easy everything went with this package, but just temporary, until I found issues with double single quotes in Postgres DB.</p><p>Schema was converted properly, including FKs and indexes, I'm not sure about sequences (auto increments). But I had issues with double single quotes. For example I had something like this in MySQL <code>It's</code>, and after conversion I ended up with <code>It''s</code> in PostgreSQL.</p><p>Configuration is so simple, you just populate provided example XML or JSON configuration file. After that you can execute PHP script with reference to one of those files, and you end up with data in new DB.</p><p>I'm pretty sure this will be solved, because package is under development, and I see some new commits are there. So I'm almost sure you should try this package if you decide to do migration.</p><h3 id="mysql-postgresql-converter-and-few-more-steps">mysql-postgresql-converter and few more steps</h3><p>Secondly I went with more manual work, but I got everything in place.</p><ul><li>First of all I ran migrations to create tables in new DB, because I already had DB migrations files in project.</li><li>Then I used <code>mysqldump</code> without create statements as starting point:</li></ul><pre><code class="language-bash">mysqldump --compatible=postgresql --default-character-set=utf8 --no-create-info -r dumpfile.sql -uroot yourdb</code></pre><ul><li>After that I used <a href="https://github.com/lanyrd/mysql-postgresql-converter">lanyrd/mysql-postgresql-converter</a> to convert insert statements to correct format:</li></ul><pre><code class="language-bash">python ~/Desktop/mysql-postgresql-converter/db_converter.py dumpfile.sql dumpfile.psql</code></pre><ul><li>Next step was text editor, and reorganisation of inserts to avoid FK exceptions, you have to do this on your own...</li><li>At first I thought this would be the last step. So execute your SQL against new db:</li></ul><pre><code class="language-bash">psql -d newdb &lt; dumpfile.psql</code></pre><ul><li>After that I found out I had issues with 'AUTO INCREMENTS' or sequences in Postgre. I wasn't able to execute new insert because of this. A few hours later, after some digging I found this is expected issue, and it's covered in PostgreSQL wiki <a href="https://wiki.postgresql.org/wiki/Fixing_Sequences">Fixing Sequences</a>.<br>So you have to execute some query that will return a list of queries for fixing sequences:<br></li></ul><pre><code class="language-sql">SELECT 'SELECT SETVAL(' ||
       quoteliteral(quoteident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
       ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
       quoteident(PGT.schemaname)|| '.'||quoteident(T.relname)|| ';'
FROM pg_class AS S,
     pg_depend AS D,
     pg_class AS T,
     pg_attribute AS C,
     pg_tables AS PGT
WHERE S.relkind = 'S'
    AND S.oid = D.objid
    AND D.refobjid = T.oid
    AND D.refobjid = C.attrelid
    AND D.refobjsubid = C.attnum
    AND T.relname = PGT.tablename;</code></pre><p><br>	You end up with something like this:<br></p><pre><code class="language-sql">SELECT SETVAL('public.tablenameidseq', COALESCE(MAX(id), 1) ) FROM tablename;</code></pre><h3 id="conclusion">Conclusion</h3><p>Based on some articles I've read I expected noticeable performance difference, but 9.5 is pretty close to MySQL 5.7. It was harder than expected, but I'm happy I did it. Because Postgre expects correct types I've found out immediately I've had some errors in my validators. And most important it was fun, and I learned something new!</p>]]></content:encoded></item><item><title><![CDATA[Deployer - Capistrano for PHP projects]]></title><description><![CDATA[<p>You want to build your custom VPS and deploy your code easily?</p><p>You can consider building git deploy solution with git hooks and scripts on your server or you can go with something Ruby developers do for a while, execute a bunch of commands over ssh.</p><p>As someone who is</p>]]></description><link>https://blog.djordjekovacevic.com/articles/deployer-capistrano-for-php-projects/</link><guid isPermaLink="false">5eb9a80049a1a60001bf2e69</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Mon, 14 Mar 2016 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>You want to build your custom VPS and deploy your code easily?</p><p>You can consider building git deploy solution with git hooks and scripts on your server or you can go with something Ruby developers do for a while, execute a bunch of commands over ssh.</p><p>As someone who is developing in Ruby I'm in contact with capistrano pretty much every day, and I must admit it's great tool for deploying your code to live servers.</p><p><a href="http://deployer.org/">Deployer</a> is exact match written in PHP. I discovered it few months ago, and finally tested it, and I'm happy to share some thoughts.</p><p>It's absolutely possible to use capistrano for deploying PHP project, because tool actually runs on your local machine, and execute shell commands over ssh on the server. But it is interesting to have deployment configuration in same language as project, it has less dependencies in development environment.</p><p>First thing I noticed is syntax that recalls Capistrano 2.x memories. For example running <code>deploy</code> task on servers marked as <code>production</code>:</p><p>capistrano 2.x cli command:</p><pre><code class="language-bash">cap deploy production</code></pre><p>capistrano 3.x cli command:</p><pre><code class="language-bash">cap production deploy</code></pre><p>deployer cli command:</p><pre><code class="language-bash">dep deploy production</code></pre><p>Other than that, everything else is similar too. So <code>dep</code> expects <code>deploy.php</code> file in current working directory, and it will use that file for configuration.</p><p>Keep in mind your server must have all dependencies for running specified tasks, e.g. <code>git</code> for fetching code from the repo.</p><h3 id="installation">Installation</h3><p>Install globally and use it anywhere:</p><pre><code class="language-bash">curl -sO http://gordalina.github.io/cachetool/downloads/cachetool.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep</code></pre><p>Or install it as a project's dependencie:</p><pre><code class="language-bash">composer require deployer/deployer:^3.0</code></pre><p>This way you can even put it in <code>"require-dev"</code> block and get functions recognized by your IDE or editor of choice, and avoid installing it on live servers too.</p><h3 id="configuration">Configuration</h3><p>Consult <a href="http://deployer.org/docs">official documentation</a> and discover how to define variables and use existing recipes.</p><p>This is configuration I've used during this test:</p><pre><code class="language-php">&lt;?php
require 'recipe/laravel.php';
set('repository', 'git@example.com:user/repo.git');
set('http_user', 'deploy');server('production', '127.0.0.1')
    -&gt;user('deploy')
    -&gt;env('deploy_path', '/home/deploy/apps/djordjekovacevic.com')
    -&gt;identityFile()
    -&gt;stage('production')
    -&gt;forwardAgent();</code></pre><p>So I pulled existing recipes for laravel, which will do those things for you:</p><ul><li>create directories structure</li><li>pull latest code from your git repo</li><li>pull <code>composer</code></li><li>install dependencies</li><li>symlink <code>storage/</code> and <code>.env</code> from <code>shared/</code> dir</li><li>symlink <code>current</code> to release dir if everything went well</li></ul><p>By default deployer will deploy <code>mater</code> branch, but you can override that with optional param <code>tag</code>:</p><pre><code class="language-bash">dep deploy production --tag=my-test-branch</code></pre><p>I used my ssh key to identify myself instead of password, and I also forwarded my identity agent to enable pulling from my git repo with same key instead of having one more key sitting on the server. Keep in mind that this setup require correct configuration of ssh agent on your server, your public key inside <code>.ssh/authorization_keys</code> file on the server and your git repo inside <code>.ssh/known_hosts</code> file.</p><h4 id="reset-you-opcache-after-deploy">Reset you OPcache after deploy</h4><p>I used PHP-FPM 7, and by default it has Zend OPcache enabled. So after deployment you have to reset cache, or you won't be able to see changes for a while!</p><p>There are two possibilities, reloading FPM process (this operation require sudoing), or running reset via PHP. I've chose second, because I have correct privileges to execute that code without sudo.</p><p>I did it with <a href="https://github.com/gordalina/cachetool">gordalina/cachetool</a> package. You install it on the server and create additional task that looks like this:</p><pre><code class="language-php">task('opcache:reset', function () {
    run('php cachetool.phar opcache:reset --fcgi=/run/php/php7.0-fpm.sock');
});
after('deploy', 'opcache:reset');</code></pre><p>I mean it's so fun for me, I'll consider VPS instead of PaaS solutions :)</p>]]></content:encoded></item><item><title><![CDATA[Blog styles updated - round one]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>It's been a while since I've blogged last time. In meantime I've upgraded styling of my blog.</p>
<p>Mainly because previous styles wasn't mobile friendly.</p>
<p><em>This is just a reminder for me how it looked before this upgrade.</em></p>
<hr>
<p>I had a lot of ideas, but not enough time to realize all</p>]]></description><link>https://blog.djordjekovacevic.com/articles/blog-styles-updated-round-one/</link><guid isPermaLink="false">5eb9a60f49a1a60001bf2e41</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Tue, 01 Mar 2016 19:25:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>It's been a while since I've blogged last time. In meantime I've upgraded styling of my blog.</p>
<p>Mainly because previous styles wasn't mobile friendly.</p>
<p><em>This is just a reminder for me how it looked before this upgrade.</em></p>
<hr>
<p>I had a lot of ideas, but not enough time to realize all of them.</p>
<p>So I decided to go with small subset in first round, and achieve better readability on small devices.</p>
<p>I hope this article will get follow up. With further improvements.</p>
<h5 id="originallook">Original look:</h5>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/original-home-page.png" alt="original-home-page"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/original-article-img-tables.png" alt="original-article-img-tables"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/original-article-code.png" alt="original-article-code"></p>
<h5 id="newlookondesktop">New look on desktop:</h5>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-desktop-home-page.png" alt="new-desktop-home-page"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-desktop-article-img-tables.png" alt="new-desktop-article-img-tables"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-desktop-article-code.png" alt="new-desktop-article-code"></p>
<h5 id="newlookonmobile">New look on mobile:</h5>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-mobile-home-page.png" alt="new-mobile-home-page"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-mobile-article.png" alt="new-mobile-article"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/new-mobile-navigation.png" alt="new-mobile-navigation"></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PHP cloud hosting comparison (OpenShift vs Heroku vs Fortrabbit)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I want PHP 5.6+, so I did some basic testing of those services to pick cheep and good solution to host my blog.<br>
OpenShift because I use it and it's free for 3 small gears, it was pretty good solution few years ago.<br>
Heroku because I used it for</p>]]></description><link>https://blog.djordjekovacevic.com/articles/php-cloud-hosting-comparison-openshift-vs-heroku-vs-fortrabbit/</link><guid isPermaLink="false">5eb9312849a1a60001bf2e21</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Tue, 01 Mar 2016 19:08:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I want PHP 5.6+, so I did some basic testing of those services to pick cheep and good solution to host my blog.<br>
OpenShift because I use it and it's free for 3 small gears, it was pretty good solution few years ago.<br>
Heroku because I used it for Ruby on Rails projects and they support multiple languages (even multiple build packs for one project)!<br>
I used FortRabbit too, so I decided to test theirs new apps.</p>
<hr>
<p>I used basic Laravel 5.2 with tree routes for this test:</p>
<ul>
<li><code>/</code> - Default route that ships with Laravel installation</li>
<li><code>/test</code> - Hits method on TestController, pass one var to view and render it</li>
<li><code>/info</code> - This route call <code>phpinfo()</code></li>
</ul>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/heroku_shirt.jpg" alt="heroku_shirt"></p>
<h3 id="tldr">TL;DR</h3>
<p>Just compare FortRabbit's and Heroku's features and prices and chose one of them.<br>
I prefer Heroku, but it was easier to transfer to FortRabbit because of MySQL support.<br>
You know what, I'll probably continue further research and probably transfer to Heroku or something else (eg. Digital Ocean VPS and deploy with <a href="http://deployer.org/">Deployer</a>). Right now FortRabbit is much better solution than OpenShift, step in right direction :)</p>
<h6 id="openshiftphp54andlaravel50currentsetupvsfortrabbitphp56andlaravel52futuresetup">OpenShift PHP 5.4 and Laravel 5.0 (current setup) vs FortRabbit PHP 5.6 and Laravel 5.2 (future setup)</h6>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/blog_fortrabbit_cloudflare.txt">OpenShift PHP 5.4 and Laravel 5.0 + CloudFlare</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/blog_fortrabbit.txt">FortRabbit PHP 5.6 and Laravel 5.2</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/blog_fortrabbit_cloudflare.txt">FortRabbit PHP 5.6 and Laravel 5.2 + CloudFlare</a></li>
</ul>
<h3 id="openshift">OpenShift</h3>
<blockquote>
<p>&quot;There ain't no such thing as a free lunch&quot;</p>
</blockquote>
<p>I'm not sure about paid version but they simply don't maintain their service. It's all about OpenShift v3 these days... But it's free, so I decided to give it a try and benchmark <a href="http://djordjekovacevic.com/articles/run-laravel-5.1-on-openshift">this setup</a> against other opponents.</p>
<p><strong>pros</strong>:</p>
<ul>
<li>Free for 3 small gears</li>
<li>MySQL 5.5 with 1GB of storage (use 1 of your 3 gears)</li>
<li>Don't require any payment information for free plan</li>
<li>Simple cronjob support via predefined file structure</li>
</ul>
<p><strong>cons</strong>:</p>
<ul>
<li>Idles after 24 hours</li>
<li>First request after idle almost always fails</li>
<li>You have to use 3rd party cartridges for beyond outdated default cartridges</li>
</ul>
<p><strong>Results (Nginx &amp; PHP 5.6.16 @ free OpenShift - 3 gears scaled):</strong></p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Concurrency</th>
<th>Requests</th>
<th>Requests/sec</th>
<th>Failed requests</th>
<th>Fastest request[ms]</th>
<th>Slowest request [ms]</th>
</tr>
</thead>
<tbody>
<tr>
<td>/</td>
<td>5</td>
<td>100</td>
<td>10.91</td>
<td>0</td>
<td>419</td>
<td>1384</td>
</tr>
<tr>
<td>/</td>
<td>10</td>
<td>1000</td>
<td>6.17</td>
<td>0</td>
<td>1036</td>
<td>6971</td>
</tr>
<tr>
<td>/test</td>
<td>5</td>
<td>100</td>
<td>9.4</td>
<td>0</td>
<td>510</td>
<td>821</td>
</tr>
<tr>
<td>/test</td>
<td>10</td>
<td>1000</td>
<td>6.3</td>
<td>0</td>
<td>1095</td>
<td>6790</td>
</tr>
<tr>
<td>/info</td>
<td>5</td>
<td>100</td>
<td>7.54</td>
<td>87</td>
<td>628</td>
<td>1901</td>
</tr>
<tr>
<td>/info</td>
<td>10</td>
<td>1000</td>
<td>3.38</td>
<td>95</td>
<td>3292</td>
<td>6539</td>
</tr>
</tbody>
</table>
<p><strong>Complete results files:</strong></p>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n100_c5_home.txt">OpenShift n100 c5 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n1000_c10_home.txt">OpenShift n1000 c10 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n100_c5_test.txt">OpenShift n100 c5 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n1000_c10_test.txt">OpenShift n1000 c10 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n100_c5_info.txt">OpenShift n100 c5 /info</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/openshift_n1000_c10_info.txt">OpenShift n1000 c10 /info</a></li>
</ul>
<h3 id="heroku">Heroku</h3>
<p>If you ever worked with Rails you probably have some experience with Heroku. It's probably first service with git deploy I used.<br>
They provide support for multiple languages, and even Apache or nginx servers for PHP. By default Heroku setups your app to use PHP 7, but you can specify desired version in <code>composer.json</code> and they will use that version. Also you have to setup default build pack manually or Heroku may make wrong assumption because of <code>package.json</code>. You have control over your services trough <code>Procfile</code> to set web or worker or anything else.<br>
Main issue when transferring live project is eventual migration from MySQL to PostgreSQL if you want to stick with Heroku's default stack.</p>
<p><strong>pros</strong>:</p>
<ul>
<li>Up to date stack</li>
<li>Support multiple languages</li>
<li>You can use multiple build packs (eg. PHP and Node.js)</li>
<li>PostgreSQL support</li>
<li>GUI for ENV var</li>
<li>Support cron jobs via add ons</li>
<li>Fair pricing for smaller project, starts at $9</li>
</ul>
<p><strong>cons</strong>:</p>
<ul>
<li>Don't support MySQL by default (you have to use 3rd party add-ons)</li>
<li>Free plan idles after 30 minutes</li>
<li>Free plan must idle at least 6 hours per day</li>
<li>For custom domain setup you have to provide payment information, even for free plan (and custom domain is free feature)</li>
</ul>
<p><strong>Results (Apache &amp; PHP 7.0.1 @ free Heroku):</strong></p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Concurrency</th>
<th>Requests</th>
<th>Requests/sec</th>
<th>Failed requests</th>
<th>Fastest request[ms]</th>
<th>Slowest request [ms]</th>
</tr>
</thead>
<tbody>
<tr>
<td>/</td>
<td>5</td>
<td>100</td>
<td>36.62</td>
<td>0</td>
<td>119</td>
<td>254</td>
</tr>
<tr>
<td>/</td>
<td>10</td>
<td>1000</td>
<td>83.18</td>
<td>0</td>
<td>110</td>
<td>1260</td>
</tr>
<tr>
<td>/test</td>
<td>5</td>
<td>100</td>
<td>33.64</td>
<td>0</td>
<td>121</td>
<td>1169</td>
</tr>
<tr>
<td>/test</td>
<td>10</td>
<td>1000</td>
<td>41.27</td>
<td>0</td>
<td>158</td>
<td>2626</td>
</tr>
<tr>
<td>/info</td>
<td>5</td>
<td>100</td>
<td>7.54</td>
<td>82</td>
<td>511</td>
<td>672</td>
</tr>
<tr>
<td>/info</td>
<td>10</td>
<td>1000</td>
<td>10.11</td>
<td>773</td>
<td>859</td>
<td>15192</td>
</tr>
</tbody>
</table>
<p><strong>Complete results files:</strong></p>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n100_c5_home.txt">Heroku n100 c5 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n1000_c10_home.txt">Heroku n1000 c10 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n100_c5_test.txt">Heroku n100 c5 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n1000_c10_test.txt">Heroku n1000 c10 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n100_c5_info.txt">Heroku n100 c5 /info</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/heroku_n1000_c10_info.txt">Heroku n1000 c10 /info</a></li>
</ul>
<h3 id="fortrabbit">FortRabbit</h3>
<p>This is PHP exclusive stack, with git deploy and it's pretty good one. New apps are great, but they lacks some features too.</p>
<p><em>2016-01-22 Update:</em><br>
FortRabbit support workers for new apps now.</p>
<p><strong>pros</strong>:</p>
<ul>
<li>Up to date stack</li>
<li>MySQL support</li>
<li>GUI for managing PHP ini and PHP version</li>
<li>GUI for ENV var</li>
<li>Fair pricing for smaller projects, starts at 7€ (5€ app server + 2€ MySQL)</li>
</ul>
<p><strong>cons</strong>:</p>
<ul>
<li>Don't provide free plan, so you have to pay even for testing</li>
<li>Currently don't support cron jobs, or workers, it's in development for new apps, but you can check one of the <a href="http://help.fortrabbit.com/external-services#toc-crons">links here</a></li>
<li><strong>No SSH access, so you cant run <code>artisan</code> commands</strong></li>
</ul>
<p><strong>Results (Apache &amp; PHP 5.6 @ 7€ instance):</strong></p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Concurrency</th>
<th>Requests</th>
<th>Requests/sec</th>
<th>Failed requests</th>
<th>Fastest request[ms]</th>
<th>Slowest request [ms]</th>
</tr>
</thead>
<tbody>
<tr>
<td>/</td>
<td>5</td>
<td>100</td>
<td>29.28</td>
<td>0</td>
<td>109</td>
<td>687</td>
</tr>
<tr>
<td>/</td>
<td>10</td>
<td>1000</td>
<td>75.4</td>
<td>0</td>
<td>114</td>
<td>1539</td>
</tr>
<tr>
<td>/test</td>
<td>5</td>
<td>100</td>
<td>42.94</td>
<td>0</td>
<td>104</td>
<td>281</td>
</tr>
<tr>
<td>/test</td>
<td>10</td>
<td>1000</td>
<td>67.62</td>
<td>0</td>
<td>118</td>
<td>1328</td>
</tr>
<tr>
<td>/info</td>
<td>5</td>
<td>100</td>
<td>9.5</td>
<td>9</td>
<td>504</td>
<td>1822</td>
</tr>
<tr>
<td>/info</td>
<td>10</td>
<td>1000</td>
<td>9.49</td>
<td>105</td>
<td>902</td>
<td>12822</td>
</tr>
</tbody>
</table>
<p><strong>Complete results files:</strong></p>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n100_c5_home.txt">FortRabbit n100 c5 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n1000_c10_home.txt">FortRabbit n1000 c10 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n100_c5_test.txt">FortRabbit n100 c5 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n1000_c10_test.txt">FortRabbit n1000 c10 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n100_c5_info.txt">FortRabbit n100 c5 /info</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_n1000_c10_info.txt">FortRabbit n1000 c10 /info</a></li>
</ul>
<p><strong>Complete results on same instance with PHP 7.0:</strong></p>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n100_c5_home.txt">FortRabbit n100 c5 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n1000_c10_home.txt">FortRabbit n1000 c10 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n100_c5_test.txt">FortRabbit n100 c5 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n1000_c10_test.txt">FortRabbit n1000 c10 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n100_c5_info.txt">FortRabbit n100 c5 /info</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_php7_n1000_c10_info.txt">FortRabbit n1000 c10 /info</a></li>
</ul>
<p><strong>Complete results on same 35€ instance with PHP 7.0:</strong></p>
<ul>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n100_c5_home.txt">FortRabbit n100 c5 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n1000_c10_home.txt">FortRabbit n1000 c10 /</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n100_c5_test.txt">FortRabbit n100 c5 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n1000_c10_test.txt">FortRabbit n1000 c10 /test</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n100_c5_info.txt">FortRabbit n100 c5 /info</a></li>
<li><a href="http://media.djordjekovacevic.com/6-openshift-vs-heroku-vs-fortrabbit/fortrabbit_2_php7_n1000_c10_info.txt">FortRabbit n1000 c10 /info</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[My migration from Laravel 4.2 to Laravel 5.0 with git history]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>First of all I want to have my git history in place for each file.<br>
This leads me to the slightly different approach during migration than official upgrade guide suggest.</p>
<p><a href="http://laravel.com/docs/5.0/upgrade#upgrade-5.0">Official upgrade guide</a> is safest way to migrate your project, but you can try to adjust it and preserve history.</p>]]></description><link>https://blog.djordjekovacevic.com/articles/my-migration-from-laravel-4-2-to-laravel-5-0-with-git-history/</link><guid isPermaLink="false">5eb8f79649a1a60001bf2e03</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Sun, 27 Dec 2015 19:07:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>First of all I want to have my git history in place for each file.<br>
This leads me to the slightly different approach during migration than official upgrade guide suggest.</p>
<p><a href="http://laravel.com/docs/5.0/upgrade#upgrade-5.0">Official upgrade guide</a> is safest way to migrate your project, but you can try to adjust it and preserve history.<br>
This will be pretty complicated if your project is big, not structured correctly (eg. you haven't namespaces your domain logic, helpers...), not documented or not tested.</p>
<hr>
<p>I did this on my blog as a proof of concept.<br>
I thought it will be quick task, but it took more time than expected.</p>
<p>Before we dive, this was status at beginning:</p>
<ul>
<li><code>+</code> Project is relatively small</li>
<li><code>+</code> A lot of project has been namespaced</li>
<li><code>+</code> I'm the only one who worked on this, so even undocumented parts are known to me</li>
<li><code>-</code> Pretty small percentage is covered with tests</li>
</ul>
<h4 id="prepareformigration">Prepare for migration</h4>
<p>It is good idea to have dump of your project extracted for reference, before you start migration. And one fresh copy of Laravel 5 as a reference where to move files, what to add/remove/update.</p>
<p>Desktop is great place for WiP files, or you can put it somewhere else.</p>
<pre><code>mkdir ~/Desktop/laravel-migration &amp;&amp; cd $_
</code></pre>
<p>Prepare fresh copy of Laravel 5:</p>
<pre><code>composer create-project laravel/laravel laravel5 &quot;~5.0.0&quot;
</code></pre>
<p>Chose your prefered technique to export copy of your project. Just aim to have it in <code>~/Desktop/laravel-migration/my_project</code>.</p>
<p>Create new git branch where you'll commit everything related to migration, eg. <code>feature/laravel-5.0</code>.</p>
<p>You should use IDE/editor with git support and move files that way, or use <code>git mv</code> via terminal. This ensures git knows how to track history for your files.</p>
<p>At this point I have my project open in IDE and <code>~/Desktop/laravel-migration/laravel5</code> open in editor for references. At any point I can open <code>~/Desktop/laravel-migration/my_project</code> and check old version withoud switching git HEAD from my upgrade branch.</p>
<p><strong>Open <a href="http://laravel.com/docs/5.0/upgrade#upgrade-5.0">official upgrade guide</a>, you will need it for for additional details (eg. changed API of some classes, changed namespaces).</strong></p>
<p>The easiest thing to migrate is namespaced code from old project if you had it, eg. <code>app/Acme</code>. You can just continue to use it, and internally fix dependencies namespaces, eg. Eloquent models.</p>
<h4 id="movefilesanddirstonewlocation">Move files and dirs to new location</h4>
<ul>
<li><code>app/config/</code> -&gt; <code>config/</code></li>
<li><code>app/database/</code> -&gt; <code>database/</code></li>
<li><code>app/tests/</code> -&gt; <code>tests/</code></li>
<li><code>app/models/</code> -&gt; <code>app/</code> (you should namespace your models, and update namespaces in relations or load classmap, consult official update guide)</li>
<li><code>app/controllers/</code> -&gt; <code>app/Http/Controllers/</code> (and update namespaces or load classmap)</li>
<li><code>app/assets/</code> -&gt; <code>resources/assets/</code> (if you stored your assets within app dir)</li>
<li><code>app/lang/</code> -&gt; <code>resources/lang/</code></li>
<li><code>app/views/</code> -&gt; <code>resources/views/</code></li>
<li><code>app/commands/</code> -&gt; <code>app/Console/Commands</code> (don't worry about namespaces yet, fix them latter)</li>
<li><code>app/routes.php</code> -&gt; <code>app/Http/routes.php</code></li>
<li><code>app/start/*.php</code> to new places (explained in Laravel's documentation) and remove dir</li>
</ul>
<h4 id="copyfromfreshlaravel5project">Copy from fresh Laravel 5 project</h4>
<ul>
<li><code>.env</code></li>
<li><code>.env.example</code></li>
<li><code>artisan</code></li>
<li><code>phpunit.xml</code></li>
<li><code>phpspec.yml</code></li>
<li><code>server.php</code></li>
<li><code>app/Console</code></li>
<li><code>app/Commands</code></li>
<li><code>app/Events</code></li>
<li><code>app/Handlers</code></li>
<li><code>app/Http</code></li>
<li><code>app/Providers</code></li>
<li><code>app/Services</code></li>
<li><code>bootstrap/app.php</code></li>
<li><code>bootstrap/autoload.php</code></li>
<li><code>config/compile.php</code></li>
<li><code>config/filesystems.php</code></li>
<li><code>config/session.php</code></li>
<li><code>config/view.php</code></li>
<li><code>database/seeds/DatabaseSeeder.php</code> (and import your seeder calls if you used it)</li>
<li><code>public/index.php</code></li>
<li><code>resources/lang/en/passwords.php</code></li>
<li><code>resources/lang/en/validation.php</code> (merge your messages if you edited this file in your project)</li>
<li><code>storage/</code></li>
</ul>
<h4 id="removefilesanddirs">Remove files and dirs</h4>
<ul>
<li><code>app/storage/</code></li>
<li><code>vendor/</code></li>
<li><code>composer.lock</code></li>
<li><code>config/remote.php</code></li>
<li><code>config/workbench.php</code></li>
<li><code>config/*/</code> - Environment specific configurations are not supported anymore (you should extract those to ENV variables)</li>
<li><code>database/production.sqlite</code> - If you don't use SQLite in production</li>
<li><code>bootstrap/paths.php</code></li>
<li><code>bootstrap/start.php</code></li>
</ul>
<h4 id="updategitignore">Update <code>.gitignore</code></h4>
<ul>
<li>
<p>Remove <code>composer.lock</code></p>
</li>
<li>
<p>Remove <code>.env.*.php</code></p>
</li>
<li>
<p>Remove <code>.env.php</code></p>
</li>
<li>
<p>Add <code>.env</code></p>
</li>
<li>
<p>Copy <code>database/.gitignore</code> from Laravel 5</p>
</li>
</ul>
<h4 id="updatecomposerjson">Update <code>composer.json</code></h4>
<ul>
<li>
<p>Remove unnecessary dev dependencies from <code>composer.json</code> to avoid migration problems, and bring them back latter if required!</p>
</li>
<li>
<p>Upgrade your <code>composer.json</code> based on one generated in new Laravel 5 project, most important parts:</p>
<pre><code class="language-json">&quot;require&quot;: {
  &quot;laravel/framework&quot;: &quot;5.0.*&quot;
},
&quot;autoload&quot;: {
  &quot;classmap&quot;: [
    &quot;database&quot;
  ],
  &quot;psr-4&quot;: {
    &quot;App\\&quot;: &quot;app/&quot;
  }
},
&quot;autoload-dev&quot;: {
  &quot;classmap&quot;: [
    &quot;tests/TestCase.php&quot;
  ]
}
</code></pre>
</li>
</ul>
<h4 id="checkifyouneedtoupdatethosefiles">Check if you need to update those files</h4>
<ul>
<li><code>config/app.php</code> - use old <code>&quot;key&quot;</code> value as fallback option, eg. <code>env('APP_KEY', 'oldKeyValue')</code></li>
<li><code>config/auth.php</code></li>
<li><code>config/cache.php</code></li>
<li><code>config/database.php</code> - keep in mind you might need <code>'port'</code> configuration for MySQL DBs</li>
<li><code>config/mail.php</code></li>
<li><code>config/queue.php</code></li>
<li>If you used <code>laracasts/validation</code> package replace it with Laravel's form request</li>
<li>If you have anything important in <code>BaseController</code> class move it to <code>App\Http\Controllers\Controller</code> class</li>
<li>If you used <code>Illuminate\Pagination\Paginator</code> class you should probably migrate it to <code>Illuminate\Pagination\LengthAwarePaginator</code> class (follow the official guide for migrating method calls)</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[CSS trick to create square elements based on container's width]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>So you have some kind of responsive CSS grid in place, let's say Bootstrap's, and you need to create inner elements with same height as container width.<br>
Something like this:</p>
<pre><code class="language-html">&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-sm-3&quot;&gt;
    &lt;!-- Element with same height</code></pre>]]></description><link>https://blog.djordjekovacevic.com/articles/css-trick-to-create-square-elements-based-on-containers-width/</link><guid isPermaLink="false">5eb892a849a1a60001bf2dee</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Thu, 03 Dec 2015 19:06:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>So you have some kind of responsive CSS grid in place, let's say Bootstrap's, and you need to create inner elements with same height as container width.<br>
Something like this:</p>
<pre><code class="language-html">&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-sm-3&quot;&gt;
    &lt;!-- Element with same height as parent's width --&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<hr>
<p>I've had this problem few years ago, and <a href="https://github.com/mdumic">Marko Dumić<br>
</a> helped me with pure CSS solution. Since then I copy/paste this snippet to various projects.</p>
<pre><code class="language-scss">// Usage example:
// div.col-sm-3
//   div.square-size-by-width
//     div.square-size-element
//       img
.square-size-by-width {
  position: relative;
  width: 100%;
  padding-bottom: 100%;

  .square-size-element {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
}
</code></pre>
<p>Here is HTML markup from teaser with inner markup:</p>
<pre><code class="language-html">&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-sm-3&quot;&gt;
    &lt;div class=&quot;square-size-by-width&quot;&gt;
	    &lt;div class=&quot;square-size-element&quot;&gt;
	    &lt;/div
    &lt;/div
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>This ensures elements inside your grid will scale with grid and maintain proportions!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Compile assets with laravel-elixir on Laravel 4.2 or any other framework]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>You want to modernize your assets compilation before you switch to Laravel 5?<br>
Elixir is great tool on top of Gulp, and you can use it for anything with little configuration. Here is my setup for Laravel 4.2 which can be easily adapted to any other framework.</p>
<hr>
<h3 id="setupnodenpmglobalandlocaldependenciesbowerandgulpfile">Setup node,</h3>]]></description><link>https://blog.djordjekovacevic.com/articles/compile-assets-with-laravel-elixir-on-laravel-4-2-or-any-other-framework/</link><guid isPermaLink="false">5eb88c3849a1a60001bf2dd6</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Wed, 02 Dec 2015 19:06:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>You want to modernize your assets compilation before you switch to Laravel 5?<br>
Elixir is great tool on top of Gulp, and you can use it for anything with little configuration. Here is my setup for Laravel 4.2 which can be easily adapted to any other framework.</p>
<hr>
<h3 id="setupnodenpmglobalandlocaldependenciesbowerandgulpfile">Setup node, npm, global and local dependencies, Bower and Gulpfile</h3>
<ol>
<li>Ensure you have <code>node</code> and <code>npm</code> on your development machine.</li>
<li>Install Gulp globally: <code>npm install gulp -g</code>.</li>
<li>Run <code>npm init</code> to prepare <code>package.json</code> file.</li>
<li>Install <code>laravel-elixir</code> with <code>npm install laravel-elixir --save</code> to preserve that dependencies in your package.</li>
</ol>
<ul>
<li>
<p>If you want you can install bower for managing front end dependencies:</p>
</li>
<li>
<p>Run <code>npm install bower --save</code></p>
</li>
<li>
<p>Create <code>.bowerrc</code> file and set destination for bower components, eg.:</p>
<pre><code class="language-json">{
	&quot;directory&quot;: &quot;app/assets/vendor&quot;
}
</code></pre>
<ul>
<li>Run <code>node_modeules/.bin/bower init</code> to create <code>bower.json</code> with desired configuration</li>
<li>Now you can install frontend dependencies, eg.: <code>node_modules/.bin/bowewr install angular --save</code></li>
</ul>
</li>
</ul>
<ol start="5">
<li>
<p>Create <code>elixir.json</code> file and set root assets and public paths. Something like this:</p>
<pre><code class="language-json">{
  &quot;assetsPath&quot;: &quot;app/assets&quot;,
  &quot;publicPath&quot;: &quot;public/assets&quot;
}
</code></pre>
</li>
<li>
<p>Create <code>gulpfile.js</code> and add initial configuration (I don't want source maps in production):</p>
<pre><code class="language-js">var elixir = require('laravel-elixir');
var config = require('laravel-elixir').config;

var bowerDir = 'app/assets/vendor/';
var jsOutput = 'public/assets/js';
var cssOutput = 'public/assets/css';
var jsAssets = 'app/assets/js';

config.sourcemaps = !config.production;

// Example compile setup:

elixir(function (mix) {
	// MAIN STYLES
    mix
        // MAIN STYLES
        .sass('main.scss')
        
        // VENDOR STYLES
        .styles([
            'normalize.css/normalize.css',
        ], cssOutput + '/vendor.css', bowerDir)
	;
});	
</code></pre>
</li>
<li>
<p>Now you can run gulp tasks from your project, eg.:</p>
</li>
</ol>
<ul>
<li><code>gulp</code> - compile assets</li>
<li><code>gulp --production</code> - compile production assets (minified and without sourcemaps)</li>
<li><code>gulp watch</code> - Compile assets every time some source file is changed</li>
</ul>
<h3 id="addelixirhelperfunctionfromlaravel5orusecustomfunctionsforloadingstylesandscripts">Add elixir helper function from Laravel 5, or use custom functions for loading styles and scripts</h3>
<p>To achieve functionality form Laravel 5 you should copy <code>elixir</code> helper function to some file loaded by composer, eg. <code>app/helpers.php</code> and load that in <code>composer.json</code>.</p>
<pre><code class="language-php">if ( ! function_exists('elixir'))
{
	/**
	 * Get the path to a versioned Elixir file.
	 *
	 * @param  string  $file
	 * @return string
	 */
	function elixir($file)
	{
		static $manifest = null;
		if (is_null($manifest))
		{
			$manifest = json_decode(file_get_contents(public_path().'/build/rev-manifest.json'), true);
		}
		if (isset($manifest[$file]))
		{
			return '/build/'.$manifest[$file];
		}
		throw new InvalidArgumentException(&quot;File {$file} not defined in asset manifest.&quot;);
	}
}
</code></pre>
<p>If you don't plan to use elixir's version task for versioning assets you can add something like this, and relay on appended timestamp in query string (like Rails 3). Just keep in mind that <strong>Laravel 5</strong> doesn't come with HTML helpers, so this won't work after update. You'll have to switch it to something like this:</p>
<ul>
<li><strong><code>HTML::style($path.'?'.File::lastModified($file))</code></strong><br>
<code>'&lt;link href=&quot;'.$path.'?'.File::lastModified($file).'&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;'</code></li>
<li><strong><code>HTML::script($path.'?'.File::lastModified($file))</code></strong><br>
<code>'&lt;script src=&quot;'.$path.'?'.File::lastModified($file).'&quot; type=&quot;application/javascript&quot;&gt;&lt;/script&gt;'</code></li>
</ul>
<pre><code class="language-php">if ( ! function_exists('stylesheet_tag'))
{
	/**
	 * Create path from stylesheet's name
	 * Append last modification date as query string
	 *
	 * @param string $name
	 * @return string
	 */
	function stylesheet_tag($name)
	{
		$path = &quot;assets/css/{$name}.css&quot;;
		$file = public_path($path);

		if (File::exists($file))
		{
			return HTML::style($path.'?'.File::lastModified($file));
		}
	}
}

if ( ! function_exists('javascript_tag'))
{
	/**
	 * Create path from javascripts's name
	 * Append last modification date as query string
	 *
	 * @param string $name
	 * @return string
	 */
	function javascript_tag($name)
	{
		$path = &quot;assets/js/{$name}.js&quot;;
		$file = public_path($path);

		if (File::exists($file))
		{
			return HTML::script($path.'?'.File::lastModified($file));
		}
	}
}
</code></pre>
<h3 id="assetscompilation">Assets compilation</h3>
<p>I prefer to compile assets locally instead on remote machine during deployment.</p>
<p>You can adapt to anything you like. If you compile locally just run <code>gulp --production</code> before committing and pushing assets to remote.</p>
<p>Otherwise you can use <code>.gitignore</code> to avoid committing compiled assets completely and register deploy hook to run same command on server, just keep in mind this will require configured node on your server!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Run Laravel 5.1 on OpenShift]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>You can't run <a href="http://laravel.com/docs/5.1">Laravel 5.1</a> on <a href="https://www.openshift.com/">OpenShift</a> by default, because it requires PHP &gt;= 5.5.9, and OpenShift provides just PHP 5.4 support.</p>
<p>So if you want to run small presentation or test your app for free, using <strong>OpenShift Free Plan</strong>, you have to do a lot</p>]]></description><link>https://blog.djordjekovacevic.com/articles/run-laravel-5-1-on-openshift/</link><guid isPermaLink="false">5eb8890f49a1a60001bf2db9</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Djordje Kovacevic]]></dc:creator><pubDate>Sun, 29 Nov 2015 19:05:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>You can't run <a href="http://laravel.com/docs/5.1">Laravel 5.1</a> on <a href="https://www.openshift.com/">OpenShift</a> by default, because it requires PHP &gt;= 5.5.9, and OpenShift provides just PHP 5.4 support.</p>
<p>So if you want to run small presentation or test your app for free, using <strong>OpenShift Free Plan</strong>, you have to do a lot of preparation by yourself!</p>
<hr>
<p>Just keep in mind that free plans have 24h idling, which means your application will respond pretty slow if not accessed in previous 24h, because OpenShift will idle those gears.</p>
<p>One more thing to keep in mind! OpenShift gears don't share storage. So if you chose scaling you have to store assets (not versioned) externally, eg. images.</p>
<p>Thanks to <a href="http://www.boekkooi.net/">Warnar Boekkooi</a> who provided <strong>nginx</strong> and <strong>PHP</strong> cartridges we can create proper environment for <strong>Laravel 5.1</strong>. Required cartridges source code is located on GitHub:</p>
<ul>
<li><a href="https://github.com/boekkooi/openshift-cartridge-nginx">Openshift Nginx Cartridge</a></li>
<li><a href="https://github.com/boekkooi/openshift-cartridge-php">Openshift PHP Plugin Cartridge</a></li>
</ul>
<p>You should check those pages details and supported configurations. You can follow steps there for setup via command line, or continue and do it via OpenShift web console.</p>
<h3 id="openshiftapplicationcreationsteps">OpenShift application creation steps</h3>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/Screen-Shot-2015-11-28-at-23.35.16.png" alt="Screen-Shot-2015-11-28-at-23.35.16"></p>
<ol>
<li>Go to OpenShift's create application page: <a href="https://openshift.redhat.com/app/console/application_types">https://openshift.redhat.com/app/console/application_types</a>.</li>
<li>Scroll to the bottom and find in the left corner &quot;Code Anything&quot; title with input field bellow.</li>
<li>Paste URL of nginx cartridge <code>http://cartreflect-claytondev.rhcloud.com/github/boekkooi/openshift-cartridge-nginx</code>, then hit next button.</li>
<li>Populate <strong>Public URL</strong> field.</li>
<li>Chose <strong>Scaling</strong> if you want.</li>
<li>Hit &quot;Create Application&quot; button and wait for OpenShift to build your environment.</li>
<li>Copy application git repo link and continue to the application overview page.</li>
</ol>
<h3 id="addphpcartridgetoyourapplication">Add PHP cartridge to your application</h3>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/Screen-Shot-2015-11-29-at-00.04.01.png" alt="Screen-Shot-2015-11-29-at-00.04.01"></p>
<p><img src="https://blog.djordjekovacevic.com/content/images/2020/05/Screen-Shot-2015-11-29-at-00.07.23.png" alt="Screen-Shot-2015-11-29-at-00.07.23"></p>
<ol>
<li>Find on the bottom of the application overview page &quot;see the list of cartridges you can add&quot; link and click on it.</li>
<li>Scroll to the bottom of the page and find &quot;Install your own cartridge&quot; title with input field bellow.</li>
<li>Paste URL for PHP cartridge <code>http://cartreflect-claytondev.rhcloud.com/github/boekkooi/openshift-cartridge-php</code>, then hit next button.</li>
<li>On next page check details and hit &quot;Add Cartridge&quot; button and wait for OpenShift to update your environment.</li>
</ol>
<h3 id="additionalcartridges">Additional cartridges</h3>
<p>If you need anything else like DB for example use existing OpenShift cartridges (MySQL, PostgreSQL).</p>
<h3 id="prepareforlaravel51">Prepare for Laravel 5.1</h3>
<ol>
<li>Clone application repo localy</li>
<li>Go to repo's root</li>
</ol>
<h4 id="preparelaravelproject">Prepare Laravel project</h4>
<ol>
<li>Run <code>composer create-project laravel/laravel --prefer-dist</code>, and this will create project in <code>laravel</code> subdir. You are not able to create project in non empty dir!</li>
<li>Go to file manager and move all files from <code>laravel</code> subdir one step up, then remove <code>laravel</code> dir.</li>
<li>Check <code>config/app.php</code> line 81 for APP_KEY!</li>
<li>Commit changes!</li>
</ol>
<h4 id="updatenginxconfigurationtoenableprettyurls">Update NGINX configuration to enable pretty URLs</h4>
<ol>
<li>
<p>Open <code>.openshift/nginx.conf.erb</code> in some text editor (eg. Atom)</p>
</li>
<li>
<p>Find <code>server</code> block, starting at line 29 and replace it with:</p>
<pre><code>server {
    listen  &lt;%= ENV['OPENSHIFT_NGINX_IP'] %&gt;:&lt;%= ENV['OPENSHIFT_NGINX_PORT'] %&gt;;
    root    &lt;%= ENV['OPENSHIFT_REPO_DIR'] %&gt;/public;

    index index.html index.htm index.php;
    charset utf-8;

    location / {
        try_files \$uri \$uri/ /index.php?\$query_string;
    }
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    location ~ \.php$ {
      fastcgi_split_path_info ^(.+\.php)(/.+)$;
      fastcgi_pass unix:&lt;%= ENV['OPENSHIFT_PHP_SOCKET'] %&gt;;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_script_name;
      include &lt;%= ENV['OPENSHIFT_NGINX_DIR'] %&gt;/usr/nginx-&lt;%= ENV['OPENSHIFT_NGINX_VERSION'] %&gt;/conf/fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}
</code></pre>
</li>
<li>
<p>Commit changes!</p>
</li>
</ol>
<h4 id="addcomposersupport">Add composer support</h4>
<ol>
<li>
<p>Create file <code>.openshift/action_hooks/deploy</code>.</p>
</li>
<li>
<p>Give it executable permissions: <code>chmod +x .openshift/action_hooks/deploy</code>.</p>
</li>
<li>
<p>Paste this content to file:</p>
<pre><code class="language-sh">#!/bin/bash
# .openshift/action_hooks/deploy

( cd $OPENSHIFT_REPO_DIR ; composer install --no-interaction --no-dev )
</code></pre>
</li>
<li>
<p>Commit changes!</p>
</li>
</ol>
<h4 id="deploytoopenshift">Deploy to OpenShift</h4>
<p>Assuming your origin is OpenShift and you want to push local master to remote master run <code>git push</code>.</p>
<p>You should see detailed response from OpenShift about stoping PHP and NGINX, than installing all your dependencies.</p>
<p>Now go to public URL for your app and you should see Laravel 5 default page!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>