Adding user-uploaded Images - Step 1 - Storing images in the Database

There is much debate about the best way to handle images in a system. Images related to the look of the site itself are best treated as assets and stored as files in the file system with other assets. Images which are uploaded by users, however, can reasonably be base64 encoded and stored as a Binary Large OBject in the database. This will incur a significant performance hit and will require the database to be able to grow to a much bigger size then it would with simple transactional data. The advantage of putting the image in the database is that it stores all data in one place and makes backup and data management simpler. This can be a big advantage, particularly where organisations are subject to compliance issues in relation to how they store and manage user data. Many developers believe that as the system starts to scale, the optimum solution is to store the images on the file system and store the name of the image hashed in the database. This poses its own challenges. Some very large organisations use a combination of both - with caching of images used to improve performance but images are ultimately stored in the database for backup and control purposes.

This series of posts will describe how to store an image in the database as a BLOB and then render that image. For this, we will use the tennisClub extending the example to include images for members of the tennisClub. Before we can add an image for each member in the database, we must add a column in the member table to store the information. This could be done in two lines of SQL using the ALTER TABLE Sql command but this time, in order stick to the laravel approach which gives us version control of our database, we will use a database migration. Copy the following code into database\migrations\addMemberColToMemberTable.php.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddImageToMemberTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('member', function (Blueprint $table) {
            DB::statement("ALTER TABLE member ADD memberimage LONGBLOB");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('member', function (Blueprint $table) {
            $table->dropColumn('memberimage');
        });
    }
}

Now, in order to run the migration type the following command into the CLI

php artisan migrate

This will make the necessary modification to the member table - adding a blob column called memberimage. It will also give us the ability to rollback those changes if necessary.

One issue we have to address is that if you end up scaffolding the table again for any reason the scaffolder will create a validation rule in the model class which requires the Longblob to be a string. Whenever you try and upload a file and assign it to the longblob this validation rule will fire and stop you creating the object. To correct this edit the file app/Models/member.php if you see a rule which says the memberimage should be a 'nullable|string' as below - remove the |string to leave just 'nullable'

Next we will modify resources/views/member/new.blade.php to allow the user upload an image of themselves when they register as a new member in the system. Initially, we need to modify the form tag to include a multipart-form data setting. This will allow a file to be uploaded as part of the form. Replace the existing form tag at the top of the file with the following code.

        {!! Form::open(['route' => 'members.store', 'files' => 'true']) !!}

The 'files' => 'true' element simply adds multipart/form data as an attribute to the form tag which will allow users to attach a file to the form when they submit.

To allow the user to pick a file to upload we need to modify the list of fields making up the form to include an extra file upload input box. To do this edit /resources/views/members/fields.blade.php and add the following code to the list of fields.

<div class="form-group col-sm-6">
    {!! Form::label('memberimage', 'Member Image:') !!}
    {!! Form::file('memberimage', null, ['class' => 'form-control','id'=>'memberimage']) !!}
</div>

Finally to allow for the multipart form binary data to be captured, processed and stored correctly we need to add the following line to the store function in /app/http/controller/memberController.php

        $member->memberimage = base64_encode(file_get_contents($request->memberimage));
        $member->save();

Add these lines here