Ignored By Dinosaurs 🦕

laravel

Easy Markdown with Syntax Highlighting, PHP Edition

Hi there, and welcome back to this 14th installment of “I rewrote my blog in another framework that I'm interested in learning, this time in Laravel”. The trick that we'll be exploring today is that, in contrast to Python (the last version was in Django), PHP's lib story is a bit more sparse for this exact use case. However, I'm completely pleased with the outcome, so let's get busy!

PHP Markdown

Google that term and you'll find this – https://michelf.ca/projects/php-markdown/. This appears to be basically the most robust and well maintained Mardown parser for PHP, so that's where I started. It's quite simple to add to a Laravel project – composer require michelf/php-markdown and then (for the purposes of syntax highlighting) you'll want to use the MarkdownExtra class. Here's the Laravel code for rendering this article body that you're reading right now -

public function rendered_body() {
	$parser = new MarkdownExtra();
	$parser->code_class_prefix = "language-";
	return $parser->transform($this->body);
}

Pretty darn simple. The only option code_class_prefix will be explained shortly.

Syntax Highlighting

In contrast to my previous post on the matter, the PHP landscape does *not* have the most robust syntax highlighting parser in the universe at its disposal. After casting about in vain for a pure PHP solution I had one of those “I wonder if there's a javascript lib for this” moments. Turns out there are a couple..

Prismjs is the lib I chose, primarily because of the well known pubs namedropped on the front page and the fact that it has my beloved Twilight theme right out of the box.

Installation was straightforward and took about 3 minutes following the instructions on the website.

The only trick is that by default, the Markdown parser wraps its code blocks with a class of lang-$language, and we need it to be language-$language for prism to correctly work its magic. Luckily this is exactly what that configuration item is for above.

Conclusion

So that's it! Literally 10 minutes worth of work between “I wonder if there's a JS highlighter that I can use instead of trying to do this in pure PHP” and having this up and running.

#laravel #php

Hello, and welcome back to Platform.sh from scratch. In this post we'll be reconfiguring your Laravel app that we've been working on in the previous posts to use Redis as a cache and session store, rather than the default file store. We'll also install the Platform CLI and use it to SSH into our application container and get a feel for the layout of the filesystem after it's deployed to its environment.

But first, I'd like to have a brief chat about Git...


Using the tools the way they're meant to be used

Ok, we've gotten this far and we're feeling good about life, but we aren't really doing anything that mindblowing yet. We've spent two posts configuring an app to run on a new hosting vendor, whooptidoo. Now, don't get me wrong – we specified our entire project's infrastructure in code. We are free to change around our project's infrastructure however we see fit, without having to file a ticket and wait for support to change it for us. And yet, we're still working on the Git branch that represents the production state of our website, aka “Master”.

Before we continue on, let's put a little insurance in place, courtesy of Git and Platform.sh.

Go to your project admin screen and click the “Branch” button, which is the orange one in the top right. Name this new branch “dev”, or really whatever you prefer.

Now say this out loud -

  • I will never push straight to master again
  • I will never push straight to master again
  • I will never push straight to master again

I'm serious. This is important. You may have worked with Git branches before, and you might be familiar with the stress saving benefits of using them, but you've probably never worked with a hosting vendor who makes it so dead simple to really use them in your day to day development work. In fact, when I first started this job I told people that I worked for a hosting vendor. Now that I understand the power of the tools that we provide I say

Platform.sh is a software company that builds tools to make your job as a developer or web application owner easier and less stressful. We also happen to host the sites with which you use our tools.

Now that you've created that branch at Platform.sh you have a byte for byte copy of your master environment, complete with web accessible URL. Any work that you do from now on will land in that dev branch before it gets merged into master. In this way you'll be able to fully QA and test out new changes before deploying them to production.

This branch only exists at Platform.sh for now, so create and checkout a local dev branch to continue on.

git checkout -b dev

A new Redis service!

So far we haven't actually built any logic into this application, nor have we even activated Laravel's built in user authentication feature, so let's go ahead and do that. Following along with the Laravel docs, run this artisan command locally to scaffold out the files that are required.

php artisan make:auth

You can run the migration locally with php artisan migrate but you'll also want to add this to the bottom of your .platform.app.yaml file -

hooks:
  deploy: |
    php artisan migrate --force

Per the previous post, this will run the database migrations for you when you deploy your app on Platform.sh. At this point you can add, commit, and push to Platform.sh and experience the joy of having bona fide user auth in just a few minutes. Thanks Taylor!

Now that you've committed that, let's head back over to the config folder and switch from using the default file session store in favor of the Redis store. At the top of config/session.php, change the SESSION_DRIVER setting from file to redis. As long as we're at it, let's also go to config/cache.php and change the default CACHE_DRIVER setting to redis as well. Now let's set up your app to use Redis.

In your .platform/services.yaml file you're going to add a new Redis service -

rediscache:
  type: redis:3.0

and in your .platform.app.yaml file we're going to add that new service to the relationships section -

relationships:
  database: "pgsql:postgresql"
  redis: "rediscache:redis" # this is new!

This is all that's required on our end to add a new service to your project, but you'll need to “enhance” your app just a bit to make use of it. In the previous post we added to the top of the config/database.php file to enable your app to find the Postgres database that we're using. That file also contains the configuration for Redis, so go there now and change this -

$config = new Platformsh\ConfigReader\Config();

if ($config->isAvailable()){
  $pltrels = $config->relationships;
  $database = $pltrels['database'][0];
  putenv("DB_CONNECTION={$database['scheme']}");
  putenv("DB_HOST={$database['host']}");
  putenv("DB_PORT={$database['port']}");
  putenv("DB_DATABASE={$database['path']}");
  putenv("DB_USERNAME={$database['username']}");
  putenv("DB_PASSWORD={$database['password']}");
}

to this -

$config = new Platformsh\ConfigReader\Config();

if ($config->isAvailable()){
  $pltrels = $config->relationships;
  $database = $pltrels['database'][0];
  putenv("DB_CONNECTION={$database['scheme']}");
  putenv("DB_HOST={$database['host']}");
  putenv("DB_PORT={$database['port']}");
  putenv("DB_DATABASE={$database['path']}");
  putenv("DB_USERNAME={$database['username']}");
  putenv("DB_PASSWORD={$database['password']}");
  if(isset($pltrels['redis'])) {
    $redis = $pltrels['redis'][0];
    putenv("REDIS_HOST={$redis['host']}");
    putenv("REDIS_PORT={$redis['port']}");
 };
}

That is all that's required to enable your app to be able to use the Redis service in your Platform.sh environment. Add, commit, and push!

git push platform dev

While that's building, let's install the Platform CLI.


The Platform CLI

As the docs say, “the CLI is the official tool to use and manage your Platform.sh projects directly from your terminal. Anything you can do within the Web Interface can be done with the CLI.” Fun fact, the project management interface is actually an AngularJS application, and both it and the CLI interact with the same set of APIs on the backend to manage your project. Almost anything that you can do from the UI you can also do from the CLI, and vice versa.

Follow the instructions in this section to install the CLI, and do make sure you read the rest of that docs page for some more background information.

Logs!

Let's use the newly installed CLI to check out some logs, since logging is crucial to knowing what's going on inside not just your application but also the environment in which it's running. Running platform logs will give you several options for which log you'd like to inspect -

> platform logs
Enter a number to choose a log:
 [0] access
 [1] deploy
 [2] error
 [3] php.access
 [4] php

Let's check out the deploy.log and see what goes on in there -

[2016-10-03 17:23:01.523855] Launching hook 'php artisan migrate --force'.

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

So the deploy log is actually the stdout output from whatever you have in your .platform.app.yaml file in the hooks.deploy section. Pretty handy for debugging as you're building up new steps. By default, the platform logs command will just cat the entire file out to your screen, but you can also pass the name of the log that you want to access as an argument to the command, and pass it a flag --tail if you want to tail the log, like so -

> platform logs --help
Command: environment:logs
Aliases: log
Description: Read an environment's logs

Usage:
 platform environment:logs [--lines LINES] [--tail] [-p|--project PROJECT] [--host HOST] [-e|--environment ENVIRONMENT] [-A|--app APP] [--] []

Arguments:
 type The log type, e.g. "access" or "error"

Options:
 --lines=LINES The number of lines to show [default: 100]
 --tail Continuously tail the log
 -p, --project=PROJECT The project ID
 --host=HOST The project's API hostname
 -e, --environment=ENVIRONMENT The environment ID
 -A, --app=APP The remote application name
 -h, --help Display this help message
 -q, --quiet Do not output any message
 -V, --version Display this application version
 -y, --yes Answer "yes" to all prompts; disable interaction
 -n, --no Answer "no" to all prompts
 -v|vv|vvv, --verbose Increase the verbosity of messages

Examples:
 Display a choice of logs that can be read:
 platform environment:logs

 Read the deploy log:
 platform environment:logs deploy

 Read the access log continuously:
 platform environment:logs access --tail

 Read the last 500 lines of the cron log:
 platform environment:logs cron --lines 500

Merging

So your dev brnach should be done building by now. Check out that everything is working the way that you expect, and if it is, let's merge the dev branhc into master. This will constitute a production deployment. You can either click the “Merge” button in your project admin UI, or you can run this from the CLI – platform merge. This will give you some interactive output so you can confirm that you're merging into the environment that you want.

One last trick for now – run platform ssh from the root of your project. Sure enough, this will SSH you into your application's PHP container, so you can get a feel for what goes on in there. A few tips -

  • The root of the application will be /app and the user will be web.
  • Those very same logs can be found at /var/log, just like normal!
  • You can check out the generated Nginx config file at the usual location as well – /etc/nginx/nginx.conf. This can be *very* useful for debugging complex configurations in your .platform.app.yaml file.
  • You can get a list of running processes the same as normal too – top. You'll see that there's not really anything going on in there beyond what your app needs to run, since OS level processes are not running in your LXC container.
  • Every app container has a Java executable. There be dragons, but you could theoretically whip up some fairly complex setups with Java dependencies if you ever needed to.

That concludes this post, and the series! We'll dive into other features, but with what you've learned in the past 3 posts you should have about 90% of what you need to orient yourself within our product.

#platformsh #laravel

Hello (!) and welcome back to Platform.sh from scratch. In this post we'll learn about how to set up the Laravel app from the previous post to hook in to various services on Platform, starting with a database connection and moving on to using Redis as a cache and session store. Along the way we'll visit Platform.sh's “environment variables” feature, and we'll set up our first fully functioning deploy hook.

Prerequisites – go through the previous post and get that far...

Let's get started!


So the first step is to add a database to your services.yaml file. Let's choose PostgresQL, which is my personal preference for open source databases these days (mostly due to the fact that it hasn't been bought by Oracle and subsequently forked). Add this to your services.yaml file, which should currently be empty.

# adds a Postgres 9.3 service to your project
# and gives it about a gigabyte of disk space
pgsql:
  type: postgresql:9.3
  disk: 1024

And in your .platform.app.yaml add this anywhere -

# This is how you define service relationships in your application
# I personally think this should've been named "services" but such is life
relationships:
  database: "pgsql:postgresql"

As you can see, setting up your project to provision new services is super easy and as the platform matures we'll likely support several versions of any given piece of software. This will someday allow users to easily test out upgrading something like a database to a new major version in another branch without worrying about the usual hassles.

Now we need to set up our application to use these new services. This is fairly straightforward, but feels a little strange the first time so I'll walk you through the general algorithm that you'll use no matter what the framework or language you're using.

Platform.sh encodes many key pieces of information about your application into OS environment variables. If you SSH into your app container you can echo $PLATFORM_APPLICATION and get back a long string that's in essence the base64 encoded version of your .platform.app.yaml file. Same with $PLATFORM_ROUTES. This is how we store metadata about your application, and you'll make use of these variables as well to establish database connections. The basic algorithm for finding DB connection info is

  • read and decode $PLATFORM_RELATIONSHIPS into a json string.
  • parse that json string into an object and use the attributes of that object to set the connection info

So with that in mind, let's get your Laravel app wired up.


First let's make use of a nice little composer package that Platform.sh has authored in order to simplify this bit. Run composer require platformsh/config-reader and install this package. Next let's head to config/database.php and add this little snippet to the top -

php // <- leave that out, it's for syntax highlighting only
$config = new Platformsh\ConfigReader\Config();

if ($config-isAvailable()){
  $pltrels = $config->relationships;
  $database = $pltrels['database'][0];
  putenv("DB_CONNECTION={$database['scheme']}");
  putenv("DB_HOST={$database['host']}");
  putenv("DB_PORT={$database['port']}");
  putenv("DB_DATABASE={$database['path']}");
  putenv("DB_USERNAME={$database['username']}");
  putenv("DB_PASSWORD={$database['password']}");
}

You can read the source of the composer package yourself, but essentially the $config instance has properties for each of the encoded environment variables that Platform.sh adds to your environments. In the case of the relationships attribute, you might have several databases defined, so it's a simple matter of digging into that property to pull that values out.

In the case of Laravel, it makes use of the vlucas/phpdotenv package to read certain settings out of environment variables, so it's really just a matter of translating the nested variables that Platform.sh provides into what Laravel is already expecting.

I suspect it would be astonishingly easy for someone to come up with a drop in “Laravel Helper” package that would set all this up for automatically, but I haven't gotten that far just yet.

With this much code, your app is now ready to connect to the database in whichever environment you'll be deploying so go ahead and commit this.

git add . && git commit -m "adding platform db config"

There's one more blocker for your Laravel app that you'll need to take care of before you're really up and running and that's the need for the APP_KEY variable to be defined. Time to leran about Platform.sh's environment variables feature!


Platform.sh Environment Variables

You're likely familiar with the situation where you need to make use of some “privileged” data in your application, and that you don't want to store that data in Git. Or perhaps the use case is that you have different settings for certain things in development than you do in production, perhaps a DEBUG flag or something like that. The usual solution for these cases is to use OS environment variables (just like we do). We provide a feature easily setting variables that you can read in your environments, so let's set one up for the Laravel SECRET_KEY.

Head to your project's admin page and click on “configure environment”, then “Variables”. The simplest thing to do is pull the SECRETKEY out of your local .env file, so click “add variable” and give it a name of “SECRETKEY” and put the value in the “value” field. This will trigger a redeployment of your application.

Last step is to add some code in to your application to read those variables back out. Add this to the top of your config/app.php file -


$config = new \Platformsh\ConfigReader\Config();

if($config-isAvailable()){
  foreach($config->variables as $k => $v) {
    putenv("$k=$v");
  }
}

Provided you've already set up that SECRET_KEY variable, it'll be read further down that file and you're good to go, so let's commit and push to platform.

git add .
git commit -m "adding platform environment variables config"
git push platform master

So this post is already a bit long so I'll just touch on one last point quickly. Now that you have this database all hooked up and ready to go, you're likely going to want to use it! Should you want to automate the process of applying your database migrations, you can do that in a simple deploy hook.

hooks:
  deploy: |
    php artisan migrate --forcxe

The --force flag sounds scary but all it does it disable the command needing feedback which of course, you are unable to provide yourself. The other option is to SSH into the app server after deployment and run the command yourself, which I'll demonstrate in the next post.

#platformsh #laravel

Hi there and welcome back to Platform.sh from scratch. In this post we'll convert a Laravel app for use on Platform and learn a few tricks that will hopefully inform converting any app for use on Platform.


Step 1 is to get going with a new Laravel app, so follow the instructions on installing Laravel and setting up a new project. Initialize a git repo, add a new platform, and add the Platform.sh git remote to your local repo. All of this is documented in the previous post.

Now, at this point you can try and push code to us but we'll reject it because you don't have any of the Platform config files in place. Let's use the exact same routes file as the previous Silex project.

# .platform/routes.yaml
"http://www.{default}/":
  type: upstream
  upstream: "app:http"
"http://{default}/":
  type: redirect
  to: "http://www.{default}/"

So that's step one. We'll get to the services.yaml file in just a minute, but let's go ahead and stub it out – touch .platform/services.yaml.

Now let's get to work on the .platform.app.yaml file, which will define what your new Laravel app will need to run. One of the key differences between this application and the previous one is that for this one we're actually going to need some writable disk space. Laravel expects a few directories to be present (and to be writable by the web user) in order to write logs and caches and such. We glossed over that bit in the previous post, so I'll now take a moment to talk about Platform.sh's read-only filesystem.


[!info]

Aside – the read-only filesystem

Platform.sh, like some other cloud PaaS providers, utilizes a read-only filesystem. When your application is deployed, we package up a snapshot of your application code and mount it into it's environment. This means that the days of being able to edit code directly on the server, or of being able to FTP code up to the server are effectively over.

All of your app's code must be in Git in order to be deployed, which has quite a few advantages. Not least of them are accountability for who did what and when to your codebase. Of course, you likely need to have some part of your filesystem be writable for logs and file uploads, so we take care of that for you but first let me expand on the benefits of going read-only.

Benefit one – consistency

As we've discussed, Platform.sh's entire workflow is built around Git. This means that each commit has a SHA hash that identifies each unique commit within your project. If you cut a new Git branch out of another, that new branch will have the same SHA hash as the currently existing branch that you cut it from.

> ~/work/php/magento-platform-sh
> $> git checkout -b test_branch master 93783b2 # <- SHA hash of this commit
> Switched to a new branch 'test_branch'
> 
> ~/work/php/magento-platform-sh
> $> test_branch 93783b2 # <- Same SHA hash
> ~~~
> 
> Platform.sh sees that the two SHA hashes are the same and doesn't bother building your new environment's codebase again since it's already built it. It just uses the already packaged code snapshot from the original branch and creates an environment around it. This saves time, but when it really shines is when it heads in the opposite direction - on merge. When merging a feature branch into a long running develop or master branch, Platform.sh sees that the code snapshot has already been built and deploys that into your master environment. 
> 
> What this means is that you are 100% guaranteed that the code being deployed into your master environment is precisely the same code that you just tested out in your feature branch. Nobody snuck anything new in there via FTP or editing directly, so you can be confident in your deployments. This leads neatly into benefit two...
> 
> __Benefit two - security__
> 
> As you're likely aware there is a large class of exploits, particularly in PHP web apps, that take advantage of that fact that a great many of them allow write access to files that the web server will execute. This means that nefarious users can sometimes find security holes that will allow them to upload executable PHP files to the server and then use those files to gain "elevated privileges", another way of saying "hack your server". With a read-only filesystem, many of those exploits are blocked before they can even happen.
> 
> __Lastly, of course you need to write some files__
> 
> So yes, your web app likely has something in there that needs to be writable. It might be for uploads or it might be for caching or it might be for logs. We got you covered, but you have to specify which directories to make writable in your `.platform.app.yaml` config file. 

So I'm just going to drop the `.platform.app.yaml` in here and explain it bit by bit.

~~~yaml
# the name of this particular app, remember that we allow you
# to create a project out of 1 or more apps, so this gives our
# Laravel app a name...
name: app
type: php:7.0 # SSIA
build:
  # Same as before, this alerts our system to look for a composer.json
  # or composer.lock file and install the dependencies defined therein
  flavor: composer
# basic web configuration for this particular app. Laravel apps have a
# "public" folder that serves as the web docroot.
web:
  locations:
  "/":
    root: "public"
    index:
      - index.php
    allow: true
    passthru: "/index.php"
# How much disk space to allot to this app. 
disk: 2048
# This is where you define your writable file system paths. the keys are the
# paths in your app that need to be writable/uploadable. The values are always
# going to be named "shared:files/$WHATEVER_HERE", where "WHATEVER_HERE" can be
# any arbitrary identifier.
mounts:
  # Laravel uses a directory off the root called "storage" for logs and cache.
  "/storage/app/public": "shared:files/storage/app/public"
  "/storage/framework/views": "shared:files/storage/framework/views"
  "/storage/framework/sessions": "shared:files/storage/framework/sessions"
  "/storage/framework/cache": "shared:files/storage/framework/cache"
  "/storage/logs": "shared:files/storage/logs"
  # And another cache directory here.
  "/bootstrap/cache": "shared:files/bootstrap_cache"

So this brings us to a decision point. Inside the storage directory are nested a few more directories. Laravel sets these directories up for you and then drops a .gitignore inside each of them. This is handy, but presents a small challenge to Platform.sh. Any directories that you declare as writable (or mountable) will be emptied out on the first build and deploy. This means that those nested directories that exist in your git repo will be deleted and you'll be left with an empty storage directory.

This will cause you some headaches when you try to deploy your Laravel app. There are two solutions to this – either add each directory that needs to be writable inside of storage to the mounts directive or recreate them in a “deploy hook” instead. We'll go with option A but I want to introduce you to hooks, so here's what option B looks like -

# .platform.app.yaml, after all the rest ...
mounts:
  # Laravel uses a directory off the root called "storage" for logs and cache.
  "/storage": "shared:files/storage"
  # And another cache directory here.
  "/bootstrap/cache": "shared:files/bootstrap_cache"
hooks:
  deploy: |
    mkdir -p storage/app/public
    mkdir -p storage/framework/views
    mkdir -p storage/framework/sessions
    mkdir -p storage/framework/cache
    mkdir -p storage/logs

This second method here took quite a bit of trial and error to figure out, and either method is valid. It would've taken me a bit if trial and error to go with option A as well, since I'm not very familiar with Laravel.

Now I'll take a moment to explain what hooks are.

Aside – hooks

Hooks are what they sound like – commands that will execute at certain points in the deployment lifecycle. In this case, we have two – build and deploy.

Build hooks run while your app is being packaged up, before it's sent to the application's environment. The filesystem is still writable at this point, so if you need to make any modifications to the file structure of your project, this is your chance to do so. Since your project doesn't have an environment yet, it doesn't yet have access to the various services that you've declared in services.yaml. So, no database is available at this point.

Deploy hooks run after your project has been mounted into the app environment. The versioned file system is no longer writable, but you do have access to your services at this point. This is typically when you do things like migrate databases or clear caches.

So you might notice that we're performing those mkdir calls in the deploy hook, which seems to contradict what I just said. However, those directories are being created inside of what you've declared as a writable directory in the mounts directive, so nothing breaks and indeed you have access to that mounted directory, where you wouldn't have had access in the build hook.

Here's the full .platform.app.yaml at this point, for reference.

name: app
type: php:7.0
build:
  flavor: composer
disk: 2048
web:
  locations:
    "/":
      root: "public"
      index:
        - index.php
      allow: true
      passthru: "/index.php"
mounts:
  # Laravel uses a directory off the root called "storage" for logs and cache.
  "/storage/app/public": "shared:files/storage/app/public"
  "/storage/framework/views": "shared:files/storage/framework/views"
  "/storage/framework/sessions": "shared:files/storage/framework/sessions"
  "/storage/framework/cache": "shared:files/storage/framework/cache"
  "/storage/logs": "shared:files/storage/logs"
  # And another cache directory here.
  "/bootstrap/cache": "shared:files/bootstrap_cache"

So save that, commit to git and push it to your Platform git remote and you should be on your way. You can find the full repository here on Github. The most important difference between that repo and this project is that we have yet to set this project up with a database connection, which we'll do in the next post.

#platformsh #laravel

Hi there and welcome back to Platform from scratch. Today we're going to take a very simple Laravel application that will make use of Postgres on the backend as a database.

The complete example application can be found here – https://github.com/JGrubb/platformsh-laravel-example

The very first step of this will be to add in the appropriate .platform/services.yaml file. This file was left intentionally empty in the setup for this Laravel application, as we didn't have a need for a working database and were just getting our app set up and running. Now however, we're going to add a very simple configuration into services.yaml.

# This is the "name" of this service and can be any arbitrary string.
# You could name this "foo" and that's the name you'd use in your
# .platform.app.yaml file in the next step.
pgsql:
  # This is the actual service you'll be using.
  type: postgresql:9.3
  # How much space you want to give this in megabytes. 
  # 1 gig will get us going
  disk: 1024

Over in .platform.app.yaml we'll add in the relationships section to the file.

relationships:
  # This config takes the form of "relationship_name: service_name:driver"
  #
  # Instead of "database", you could also call this "bar" and that's how it'll
  # show up in the $PLATFORM_RELATIONSHIPS environment variable. I'll show
  # you how that manifests itself in just a moment.
  #
  # "pgsql" in this case is the name you gave it in services.yaml and
  # "postgresql" is the part that you can't just arbitrarily name. That
  # is the hint to our container build system that you need a PG database
  # service.
  database: "pgsql:postgresql"

Commit this and push it up to your platform remote. This will trigger a rebuild of your application, and when that's done let's SSH into your environment with the Platform CLI – $ platform ssh.

Once you're in, try this – echo $PLATFORM_RELATIONSHIPS. You'll get a big base64 encoded string as a result. This is because you can't set complex objects or even JSON as an environment variable, so let's decode that by piping it to base64 --decode – echo $PLATFORM_RELATIONSHIPS | base64 --decode

This should give you back something like

{"database": [{"username": "main", "password": "main", "ip": "250.0.96.171", "host": "database.internal", "query": {"is_master": true}, "path": "main", "scheme": "pgsql", "port": 5432}]}.

sidebar

Just for the fun of it, let's try this in the relationships section -

relationships:
  dark_chocolate: "pgsql:postgresql"

Sure enough —

web@4ikq2xigwlw5s-master--app:~$ echo $PLATFORM_RELATIONSHIPS | base64 --decode
{"dark_chocolate": [{"username": "main", "password": "main", "ip": "250.0.96.171", "host": "big_daddy.internal", "query": {"is_master": true}, "path": "main", "scheme": "pgsql", "port": 5432}]}

So the basic gist of how you establish a connection to any kind of service that you set up in services.yaml should now be clearer than it was before, and we'll now set about adding the code to our Laravel app that will make use of these environment variables.

// at the top of config/database.php. This will decode the base64
// encoded envvar and expand it into the variables that Laravel is
// expecting.
if ($relationships = getenv('PLATFORM_RELATIONSHIPS')){
  $pltrels = json_decode(base64_decode($relationships), TRUE);
  $database = $pltrels['database'][0];
  putenv("DB_CONNECTION={$database['scheme']}");
  putenv("DB_HOST={$database['host']}");
  putenv("DB_PORT={$database['port']}");
  putenv("DB_DATABASE={$database['path']}");
  putenv("DB_USERNAME={$database['username']}");
  putenv("DB_PASSWORD={$database['password']}");
}

This particular piece of code is not Postgres specific, and in fact will work just fine with MySQL as well. The beauties of abstraction...

The final step in this process is optional, but if you want to have artisan migrate the database on deploy rather than logging into the server and running it manually you'd add this to the bottom of your hooks.deploy in .platform.app.yaml —

hooks:
  deploy:
    # other commands
    php artisan migrate --force

The --force flag will allow migrate to run in a “production” environment.

There is one final step to be aware of, and that's that the pdo_pgsql extension is not enabled by default in the PHP containers. You'll need to add this somewhere in .platform.app.yaml -

runtime:
  extensions:
    - pdo_pgsql

If you were using MySQL, this step would not be needed as pdo_mysql is enabled by default. Indeed, if you're using Postgres, you can disable the MySQL extension if you wish -

runtime:
  extensions:
    - pdo_pgsql
  disabled_extensions:
    - pdo_mysql

For reference, here's the complete .platform.app.yaml —

name: app
type: php:5.6
runtime:
  extensions:
    - pdo_pgsql
build:
  flavor: composer
disk: 2048
web:
  locations:
    "/":
      root: "public"
      index:
        - index.php
      allow: true
      passthru: "/index.php"
mounts:
  "/storage": "shared:files/storage"
  "/bootstrap/cache": "shared:files/bootstrap_cache"
relationships:
  database: "pgsql:postgresql"
hooks:
  deploy: |
    mkdir -p storage/app/public
    mkdir -p storage/framework/views
    mkdir -p storage/framework/sessions
    mkdir -p storage/framework/cache
    mkdir -p storage/logs
    php artisan migrate --force

#platformsh #postgres #laravel

Expository info (skippable)

So, for nigh 6-7 years now I've been a Rails enthusiast. I bought the PragProg AWDWR book when it was covering Rails 2.x and had the beach and hammock on the cover, and then proceeded to take years to figure out everything the book was actually talking about from the bottom of the stack to the top. I find it very enjoyable to be able to get my ideas out (in code), and Rails is still one of the cushiest frameworks around in terms of ease of use. It's almost as though it were a central tenet of it's philosophy...

For nigh 6 years now, I've been a Drupal professional. Drupal isn't the sexiest platform on the block, but it's been marketed in an absolutely genious manner and has seen incredible traction in sectors that have jobs-o-plenty — government, educational institutions, publishing, basically anything that needs a robust CMS. I do not, however, find it very enjoyable to work with. The aspects of Drupal that are the most “Drupalistic” – the Form API, render arrays, “configuration as convention” – just feel so out of sync with how the rest of the development world does things. However, see the above point about jobs-o-plenty, and there is still plenty of fun code to be written around the edges of a larger and more mature Drupal installation.

Drupal 8 is going to come out some day though, and with it will come the invalidation of pretty much everything the wider Drupal community has ever known about writing code for Drupal. It's going to require those of us who enjoy writing code to basically start from scratch with a new framework, only this framework is GIGANTIC and carries with it plenty of interesting architecture opinions and non-opinions alike from it's 12 year history. So I suspect I'm not alone in thinking “if I have to learn an entirely new thing anyway, will learning D8 be the most effective use of my limited time?”

—

So, given these factors, I've been watching Laravel for a long time, since V3. I haven't really started playing with it until this most recent major release – V5. My initial thoughts were “oh, this is basically PHP on Rails” – I mean this as a complement. But, since I already knew enough about Rails to be effective and have fun, why not spend the time learning Rails better? So that's what I did before the Drupal community at large took up “get off the island” as a mantra.

For the last few months though, I've spent a lot of time building small-ish things with Laravel, and have even had the team begin a Lumen-backed project. I've been going back through the old Laravel podcasts, since I think podcasts are a wonderful way to absorb the philosophy of whichever developer they have on. (Kudos to you Taylor for consistently being on the podcast about your framework. That actually says a lot about your dedication to moving this whole thing forward).

Ok, with that long winded exposition out of the way, a few thoughts...


The aforementioned ruminations

  • I like how Laravel is immediately familiar to someone who's worked with Rails down to the API of say, establishing relationships in Eloquent or the up and down methods on migrations. Almost any server-side framework that has come since Rails in any language has been either an embrace of or a reaction to its widely marketed “opinions”. I was listening to an older Laravel podcast where the topic was some haters in the PHP community (supposedly) accusing Laravel of being “too Rails-y” or something. I consider this a plus to the framework (obviously), and I think designing a framework around the opposite paradigm – trying to not make it too Rails-y – immediately builds walls around our various yards in the pan-linguistic developer community. If the rules and the terminology are similar, even if it's in a different programming language, we can all get up to speed with the new thing faster and stop wasting our time learning new concepts that are actually the exact same concepts by a new name.
  • I like how Laravel takes Rails' opinionated-ness and actually goes a step further. Rails does not have Auth built into the core. For some reason Rails has Active Job and Turbolinks and Coffeescript, but not Auth. I'd challenge anyone out there to find me a public Rails app without Auth. Building Auth into the framework is an obvious move, and I thank Laravel for doing so. Same goes for billing, or having a Redis cache driver, or having built in support for queued jobs. I was listening to a really old Laravel podcast last night, and Taylor basically described exactly the interface that now exists in L5 – a Billable trait that only uses Stripe and that's that. The kind of simplicity that can only come out of being opinionated. The kind of simplicity that Drupal can never have precisely because of it's lack of opinions in the architecture.
  • Though it's got its share of fans, I don't get a sense of religion off of Laravel (yet). There's a religion around Drupal, and I think religion is mostly a dangerous thing in that it encourages its followers to follow, but not to ask too many questions of its leaders. The relatively insane pace of development and major version bumps and refactoring of the file structure in these bumps is a double edged sword, but it kinda keeps everyone from getting too set in the “old ways” and offers to me a subconscious clue that Laravel is still casting about for the “righter” way to do things, that nothing is too sacred yet.
  • With that, I like that Laravel is finally offering a LTS release. This will allow me to actually sell it to my boss, and feel better about investing time in really learning the thing. I'm feeling more than a little burned by Angular (talk about learning a mountain of new jargon), but at least the whole Angular 2 flap is a good learning experience for evaluating a new technology. “Be skeptical of shiny new things.”

#laravel #rails

I'm sure this is obvious, but I just spent too long trying to figure this one out. The mutator function wasn't even being called.

Make sure that the field you want to mutate is included in your model's $fillable array, else the ORM doesn't care that you want to do things to it.

#laravel