June 26, 2015

JavaScript HighCharts in Laravel 5 via Blade

Laravel 5's Blade templating engine is awesome. It can be used to directly inject PHP variables into a HTML5 template to display Eloquent ActiveRecord entities right away to the client. Besides this obvious intended use, escaping those variables can be powerful, as we will see.

HighCharts is a JavaScript library that can be used to display beautiful visualizations of sets of data. If you're here and reading this, you are probably looking for a way to integrate your Laravel PHP app with this JS library.

I started by checking out some HighCharts PHP ports on Packagist, but at the time of this writing, the few such packages have either gone unmaintained into obsolescence, or are overkill for what can be elegantly accomplished with a few lines of code. The trick here is to take advantage of Blade template escaping.

Let's look at the official HighCharts example, as given in its native JavaScript + jQuery:


$(function () { 
    $('#container').highcharts({
        chart: {
            type: 'bar'
        },
        title: {
            text: 'Fruit Consumption'
        },
        xAxis: {
            categories: ['Apples', 'Bananas', 'Oranges']
        },
        yAxis: {
            title: {
                text: 'Fruit eaten'
            }
        },
        series: [{
            name: 'Jane',
            data: [1, 0, 4]
        }, {
            name: 'John',
            data: [5, 7, 3]
        }]
    });
});

The following HTML snippet with the above JS in an HTML page will display a simple chart:


<div id="container" style="width:100%; height:400px;"></div>

That's cool and all, but that's a lot of JS for a PHP app, and it doesn't solve actually getting the database variables to HighCharts, unless we feel like resorting to some funky AJAX hackery... Fear not, there is a better way. Let's start by reconstructing the JSON config structure from the JS HighCharts into a PHP array. Create a controller, say DashboardController and make this method:


public function index() {

    $yourFirstChart["chart"] = array("type" => "bar");
    $yourFirstChart["title"] = array("text" => "Fruit Consumption");
    $yourFirstChart["xAxis"] = array("categories" => ['Apples', 'Bananas', 'Oranges']);
    $yourFirstChart["yAxis"] = array("title" => array("text" => "Fruit eaten"));

    $yourFirstChart["series"] = [
        array("name" => "Jane", "data" => [1,0,4]),
        array("name" => "John", "data" => [5,7,3])
    ];
    return view('finances.dashboard', compact('yourFirstChart'));
}

Now add the route to routes.php:

Route::get('dashboard', 'DashboardController@index');

And finally, create the dashboard.blade.php view itself. Note that you will also need the actual highcharts.js and jQuery libraries (or the stand-alone framework if you don't want to use jQuery!) - in my case they are simply declared in the parent view which is why they aren't included in the dashboard child view below. Anyways, we are now JSON encoding the $yourFirstChart array as passed in from the controller, and escaping the whole thing:


@extends('app')

@section('content')
<div id="container" style="width:100%; height:400px;"></div>
<script type="text/javascript" src="{{ asset('js/highcharts.js') }}"></script>

<script type="text/javascript">
    $(function () {
        $('#container').highcharts(
            {!! json_encode($yourFirstChart) !!}
        );
    })
</script>
@endsection

Your IDE probably won't like the syntax mixture very much, but it will inject a completely valid JSON into the <script>. And the best part is that the JSON is generated from a PHP array that can receive all the usual Eloquent database objects, thus completing the missing data link between HighCharts and Laravel. If all went well you should see the example chart in all its interactive glory like below - go ahead and click the items in the legend!



May 8, 2015

Laravel 5 Eloquent Models & Tinker Tips

Eloquent is Laravel's implementation of ActiveRecord, seen in other programming languages too, like Ruby. It insulates you from the need to use hard SQL queries in your code. Each new ActiveRecord is basically one line of a database table, containing all the columns as object attributes wrapped in a PHP model class. Creating an Eloquent Model is easy:

php artisan make:model Transaction

This creates a PHP class at app/Transaction.php which can be filled with attributes specific to that object model. Another cool thing, is that Laravel will also create a database migration for it; pretty useful since you are likely going to store this object somewhere. Now that we have a model, we can use Tinker to demonstrate how ActiveRecord queries the database.


Assigning Values


Run php artisan tinker from the command line, and let's play around with objects to manipulate our database. Values can be assigned to attributes like so:

$transaction = new App\Transaction
$transaction->debit = '3.33';
$transaction->description = 'cup of coffee';
$transaction->date = Carbon\Carbon::now();
// object is getting filled up, but not yet persisted to the database:
$transaction->save();

This is roughly equal to the MySQL command:
INSERT INTO transactions (debit, description, date) 
   VALUES ('3.33','cup of coffee', NOW());


Looking Up Values


Now that the object has been persisted back to the database, we can verify the added record by looking directly in the database, by doing something like this in MySQL:
GET * FROM transactions;

More elegantly, we can do one of several ActiveRecord queries via Tinker like so:

App\Transaction::all()->toArray();

// or find via database ID key:
$transaction = App\Transaction::find(1);

// or find by column; this returns a Laravel Collection:
$transaction = App\Transaction::where('debit', '3.33')->get();

// or return only the first result as a class object instead:
$transaction = App\Transaction::where('debit', '3.33')->first();


Mass-Assigning Values


Assigning individual values can be tedious and time-consuming. We can mass-assign values to a database row by passing an array like:

App\Transaction::create([
   'debit' => '3.33',
   'description' => 'cup of coffee'
   'date' => Carbon\Carbon::now()
]);

The ability to mass-assign values like this, requires a safeguard to protect us against exploiters using fake fields to inject data via a form. We can build a safeguard by explicitly adding an accepted array of values that are permitted to be filled via mass-assignment. The app/Transaction.php model could look something like this:

class Transaction extends Model {
   protected $fillable = [
      'debit',
      'description',
      'date',
      'credit',
      'total',
      'account'
   ];
}

Mass Assignment will now work for any combination of fillable fields.


Mass-Updating Values


The mass-assign ::create() method above is directly enacted upon an Eloquent class. To update a database record, first create an Eloquent object, then use the ->update() method on it, passing an array of values:

App\Transaction::create(['description'=>'cheap coffee', 'debit'=>'3.33']);
$tn = App\Transaction::where('description', 'cheap coffee')->first();
$tn->update(["debit" => "2.22"]);

The ->update() method will automatically save to the database!

April 28, 2015

Laravel 5 Database Migrations - The Basics

Migrations are a way to "version control" a database. There are ways to "seed" data into a database, but migrations are more concerning the schema. Previously, to set up a database, a lot of manual setup needed to be done; create a table, add some columns, set their keys, etc etc. And if the database corrupted, or got blown away, or you needed to set it up on another server or share with someone else, there would probably be a bit of manual setup again, despite potential sqldumps and schema exports.

Migrations are a neat way to get around a lot of this manual setup via PHP classes. Basically, a migration is a PHP class that represents the instructions telling the database what needs to happen during setup. Laravel has an easy way to create migrations of various types; depending on what flags are used, a new PHP class with some relevant boilerplate code will be set up. For example:

php artisan make:migration create_cool_table --create="cool"

The migration file will get created and stored in the database/migrations folder as create_cool_table.php. Note the up() and down() methods within it. The former is to implement a change to the database, and the latter is to undo it via a rollback. These two methods should be exact opposites in what they accomplish.

Actually implement a migration - the up() method:
php artisan migrate

Undo the most recent migration - the down() method:
php artisan migrate:rollback

If a rollback fails, it is likely because of a missing database package. Add it:
composer require doctrine/dbal

The Laravel framework uses the migrations table in the database to keep track of which migrations were already run in the past. A migration will never be run again, unless you roll it back, as above, to make changes to it.

Caveat: If a migration has already been run on a Production server, or a friend/co-worker's machine, it is probably better to just create a fresh migration to make further changes, instead of rolling back the migrated migration, changing it, and re-migrating it. To make a change to an existing table, we are still creating a new migration, but can use different flags to set up useful boilerplate for this case:
php artisan make:migration modify_cool_table --table="cool"

As a general rule-of-thumb, it is always easier to just migrate new stuff, than to first un-migrate some stuff, then pull in the updates, then re-migrating... Keep things sequential!

April 18, 2015

Laravel 5 Routes, Controllers and Blade Views

The Basics


PHP Artisan commands are helper functions to make development with Laravel super fast. It also has a help feature, for example, to display information and useful flags for the make:controller method:
php artisan help make:controller

Routes are located in app/Http/routes.php.

Anatomy of a route is:
Route::get('URI', 'controllerName@controllerMethod');

A controller just provides a way to pass a method, so this will work too:
Route::get('foo', function() 
{
   return "bar";
});

We can also pass a wildcard from a route to a method, and back again:
Route::get('URI/{value}', 'controllerName@valueManipulatorMethod');

Controllers are located in the app/Http/Controllers folder, named as ControllerName.php. One controller per file. Each controller can have many methods.

Create a new controller via Artisan:
php artisan make:controller ControllerName

Views are located in the resources/views folder, named as viewname.blade.php. They can also be organized in subdirectories.

Return a view from a controller method:
return view('viewname');


// These two subdirectory calls are identical:
return view('subdirectory/viewname');
return view('subdirectory.viewname');

Using data from Controllers in Views


A view itself is just plain HTML. No data is generated/manipulated in a view. We can pass variables to a view template though. In the controller:
public function method()
{
   $myVariable = "hello world";
   return view('subdirectory.viewname')->with('greeting', $myvariable);
}

And in the HTML view, using escaped data:
<h1>This is my app that says {{ greeting }} </h1>

And in the HTML view, using unescaped data (can inject unsafe data, such as javascript):
<h1>This is my app that says {{!! greeting !!}} </h1>

Can also pass multiple variables into a view, declared within an array instead. In the controller:
public function method()
{
   return view('subdirectory.viewname')->with([
      'foo' => 'hello',
      'bar' => 'world'
   ]);
}

Alternately, still using arrays:
public function method()
{
   $data = []
   $data['foo'] = 'hello';
   $data['bar'] = 'world' 

   return view('subdirectory.viewname', $data);
}

Alternately, using plain variables:
public function method()
{
   $foo = 'hello';
   $bar = 'world'; 

   return view('subdirectory.viewname', compact('first', 'last');
}

All 3 controller methods above will work in this view:
<h1>This is my app that says {{ foo }} {{ bar }} </h1>

Blade Templating


Put all boilerplate HTML in a master/layout view; let's call it master.blade.php. Insert child views like:
<p>This is the master view</p>
@yield('section1')
@yield('section2')

In a child view:
@extends('master')
@section('section1')
<p>This is a child view paragraph</p>
@stop

Loading the above child page in a browser will display:
This is the master view
This is a child view paragraph

Conditionals can be used within views, using data passed from a controller (note how data is still not created - the conditionals merely reflect what HTML to show):
@if (count($people))
<h3>People I like:</h3>
<ul>
   @foreach ($people as $person)
      <li>{{ $person }}</li>
   @endforeach
</ul>
@endif

It is also possible to construct href links with data passed from a controller:

<a href="/transactions/{{ $transaction->id }}">{{ $transaction->description }} </a>

Or pass data from the view back to the controller as an array:

<a href="/transactions/{{ action('TransactionController@show', [$transaction->id]) }}">{{ $transaction->description }} </a> 

This also works:

<a href="{{ url('/transactions, $transaction->id }}">{{ $transaction->description }} </a> 

April 4, 2015

Edit/Add to hosts file in Windows

On Mac or Linux, this is a cinch.
sudo vi /etc/hosts

On Windows, it sucks, and you will probably run into the "You cannot modify the Hosts file" error. Here's the workaround.

Navigate to Start Menu -> All Programs -> Accessories and right-click Notepad to run it as an administrator. You may be asked for your password at this point:

Navigate to the hosts file in Windows Explorer. It is located at C:\Windows\System32\drivers\etc\hosts. Right click it, and un-select the Read-Only attribute:

Now use Notepad to navigate to the hosts file. Be sure to view All Files!


Finally, change what you need to change. When done, do not just click File -> Save, but instead do File -> Save As... and overwrite the hosts file. You may again need to specify All Files as done above, else the hosts file you modified will just be saved as a .txt copy, which does nobody any good. Lastly, after saving, remember to make the hosts file Read-Only again.

March 19, 2015

Laravel 5 Environment setup

The .env file is a non-version-controlled file that can contain sensitive information such as settings and passwords. Laravel 5 comes with a .env.example file to get things started.

The way you use environment variables is by declaring them in the .env file, and then invoking them as such:
'key' => env('VALUE', 'default')
'host' => env('DB_HOST', 'localhost')

Additionally, further configuration can be set in files in the config folder. For example, all the database connection information is set up in the config/databases.php file. Everything in that folder is used to that extent, so be sure to have a peek there if you install any useful packages into your app.

Laravel error pages also change what they display based on the environment. For example, the APP_ENV variable in the .env file controls how your app interacts with the browser. If set to APP_ENV = local, there will be much more verbose error pages (useful to a developer), as opposed to APP_ENV = production, which will show only the more user-friendly "Whoops, looks like something went wrong." message! Pretty useful stuff.

March 4, 2015

Speedfan won't run on Windows 7 startup

I use Speedfan on my Windows machine to monitor fan speeds, temperatures, voltages etc for my hardware. It is extremely lightweight, clocking in at just over 3mB of memory, and more importantly is easily integrated into other software. I use it with Rainmeter, which is a beautiful way to add widgets and skins to your desktop, and is far more customizable than the native Windows widgets. One of my widgets provides information piped in from Speedfan:


Since Speedfan has been updated to work with Windows 8 binaries, I am no longer able to just toss a shortcut into the Startup folder. It will throw the following error on login; Error <SPEEDFAN Driver Not Installed> Is SPEEDFAN service started?:


The only work-around I could get working was to set up a new task in Task Scheduler. From Action -> Create Task, you can set up a new task to run on Startup.

  • In the General tab, enter a name, and be sure to select your OS to configure for.
  • In the Triggers tab, click "New", and select "Begin the task at log on" from the dropdown menu. I suggest also adding the delay task checkbox for 30 seconds. You might need to experiment with this a little bit; if the delay is too short, Speedfan still won't start.
  • In the Actions tab, actually Start Speedfan by inputting the path to the program. If you've configured Speedfan itself to start minimized, it will; no need to specify additional arguments.
  • This is enough information to get it going, so click OK.


If configured correctly, you should see something like what is above, and on your next computer startup, Speedfan should once again be running on its own.