create a photo gallery with laravel

Deven Rathore 28 min read
create a photo gallery with laravel

In this tutorial we’ll code a simple photo gallery system with Laravel. We’ll also cover Laravel’s built-in file validation, file upload, and the hasMany database relation mechanism. We will use thevalidation class to validate the data and files. Also, we’ll cover the file class for processing files. The following topics are covered in this chapter:

  • Creating an Album model
  • Creating an Image model
  • Creating an album
  • Creating a photo upload form
  • Moving photos between albums

Lets Get started…

here is the source code of this tutorial adapted to laravel 5.4
 

Creating a table and migrating albums

We assume that you’ve already defined the database credentials in the database.php file located atapp/config/. To build a photo gallery system, we need a database that has two tables: albums andimages. To create a new database, simply run the following SQL command:

CREATE DATABASE laravel_photogallery

After successfully creating the database for the application, we will first need to create the albumstable and install it in the database. To do this, open up your terminal, navigate through your project folder, and run the following command:

The preceding command will generate a migration file under app/database/migrations to generate a new MySQL table, named posts, in our laravel_photogallery database.

To define our table columns, we need to edit the migration file. After editing, the file should have the following code:

<?php

use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;

class CreateAlbumsTable extends Migration {

  /**
  * Run the migrations.
  *
  * @return void
  */
  public function up()
    {
      Schema::create('albums', function(Blueprint $table)
      {
        $table->increments('id')->unsigned();
        $table->string('name');
        $table->text('description');
        $table->string('cover_image');
        $table->timestamps();
      });
    }

  /**
  * Reverse the migrations.
  *
  * @return void
  */
  public function down()
  {
    Schema::drop('albums');
  }
}

After saving the file, we need to use a simple artisan command again to execute migrations:

php artisan migrate

If no error has occurred, please check the laravel_photogallery database for the albums table and its columns.

Let’s examine the columns in the following list:

  • id: This column is used for storing the ID of the album
  • name: This column is used for storing the name of the album
  • description: This column is used for storing the description of the album
  • cover_image: This column is for storing the cover image of the album

We’ve successfully created our albums table, so we need to code our Album model.

Creating an Album model

As you know, for anything related to database operations on Laravel, using models is the best practice. We will benefit from using the Eloquent ORM.

Save the following code as Album.php in the app/models/ directory:

<?php
class Album extends Eloquent {

  protected $table = 'albums';

  protected $fillable = array('name','description','cover_image');

  public function Photos(){

    return $this->has_many('images');
  }
}

We have set the database table name using the protected $table variable; we’ve also set the editable columns using the protected $fillable variable, which we’ve already seen and used in previous chapters. The variables that are defined in the model are enough for using Laravel’s Eloquent ORM. We’ll cover the public Photos () function in the Assigning a photo to an album section of this chapter.

Our Album model is ready; now we need an Image model and a database to assign photos to albums. Let’s create them.

 

Creating the images database with the migrating class

To create our migration file for images, open up your terminal, navigate through your project folder, and run the following command:

As you know, the command will generate a migration file in app/database/migrations. Let’s edit the migration file; the final code should be as follows:

<?php

use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;

class CreateImagesTable extends Migration {

  /**
  * Run the migrations.
  *
  * @return void
  */
  public function up()
  {
    Schema::create('images', function(Blueprint $table)
    {
      $table->increments('id')->unsigned();
      $table->integer('album_id')->unsigned();
      $table->string('image');
      $table->string('description');
      $table->foreign('album_id')->references('id')->on('albums')->onDelete('CASCADE')->onUpdate('CASCADE');
      $table->timestamps();
    });
  }

  /**
  * Reverse the migrations.
  *
  * @return void
  */
  public function down()
  {
    Schema::drop('images');
  }
}

After editing the migration file, run the following migrate command:

php artisan migrate

As you know, the command creates the images table and its columns. If no error has occurred, check the laravel_photogallery database for the users table and the columns.

Let’s examine the columns in the following list:

  • id: This column is used for storing the id of the image
  • album_id: This column is used for storing the id of the image’s album
  • description: This column is used for storing the description of the image
  • image: This column is used for storing the path of the image

We need to explain one more thing for this migration file. As you can see in the migration code, there is a foreign key. We use the foreign key when we need to link two tables. We have an albums table and each album will have images. If the album is deleted from the database, you want all its images to be deleted as well.

 

Creating an Image model

We’ve already created the images table. So, as you know, we need a model to operate database tables on Laravel. To create that, save the following code as Image.php in the app/models/directory:

note – by default models directory is loacted in app directory but we have created a models directory for ease  with composer autoload class

class Images extends Eloquent {
  
  protected $table = 'images';
  
  protected $fillable = array('album_id','description','image');
  
}

Our Image model is ready; now we need a controller to create the albums on our database. Let’s create that.

 

Creating an album

As you know from the previous chapters in this book, Laravel has a great RESTful controller mechanism. We’ll continue to use that to keep the code simple and short during development. In the next chapters, we’ll cover another great controller/routing method named Resource Controllers.

To list, create, and delete an album, we need some functions in our controller. To create them, save the following code as AlbumsController.php in the app/http/controllers/ directory:

<?php

class AlbumsController extends BaseController{

  public function getList()
  {
    $albums = Album::with('Photos')->get();
    return View::make('index')
    ->with('albums',$albums);
  }
  public function getAlbum($id)
  {
    $album = Album::with('Photos')->find($id);
    return View::make('album')
    ->with('album',$album);
  }
  public function getForm()
  {
    return View::make('createalbum');
  }
  public function postCreate()
  {
    $rules = array(

      'name' => 'required',
      'cover_image'=>'required|image'

    );
    
    $validator = Validator::make(Input::all(), $rules);
    if($validator->fails()){

      return Redirect::route('create_album_form')
      ->withErrors($validator)
      ->withInput();
    }

    $file = Input::file('cover_image');
    $random_name = str_random(8);
    $destinationPath = 'albums/';
    $extension = $file->getClientOriginalExtension();
    $filename=$random_name.'_cover.'.$extension;
    $uploadSuccess = Input::file('cover_image')
    ->move($destinationPath, $filename);
    $album = Album::create(array(
      'name' => Input::get('name'),
      'description' => Input::get('description'),
      'cover_image' => $filename,
    ));

    return Redirect::route('show_album',array('id'=>$album->id));
  }

  public function getDelete($id)
  {
    $album = Album::find($id);

    $album->delete();

    return Redirect::route('index');
  }
}

The postCreate() function first validates the posted data of the form. We’ll cover validation next. If the data is validated successfully, we will rename the cover image and upload it with a new filename, because the code overwrites files which have the same name.

The getDelete() function is deleting the album along with assigned images (which are stored in animages table) from the database. Please remember the following migration file code:

$table->foreign('album_id')->references('id')->on('albums')->onDelete('CASCADE')->onUpdate('CASCADE');

Before creating our templates, we need to define the routes. So, open up the routes.php file in theapp folder and replace the code with the following one:

<?php
Route::get('/', array('as' => 'index','uses' => 'AlbumsController@getList'));
Route::get('/createalbum', array('as' => 'create_album_form','uses' => 'AlbumsController@getForm'));
Route::post('/createalbum', array('as' => 'create_album','uses' => 'AlbumsController@postCreate'));
Route::get('/deletealbum/{id}', array('as' => 'delete_album','uses' => 'AlbumsController@getDelete'));
Route::get('/album/{id}', array('as' => 'show_album','uses' => 'AlbumsController@getAlbum'));

Now, we need some template files to show, create, and list the albums. First, we should create the index template. To create that, save the following code as index.blade.php in the recources/views directory:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Awesome Albums</title>
    <!-- Latest compiled and minified CSS -->
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Latest compiled and minified JavaScript -->
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
    <style>
      body {
        padding-top: 50px;
      }
      .starter-template {
        padding: 40px 15px;
      text-align: center;
      }
    </style>
  </head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
      <button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".nav-collapse">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/">Awesome Albums</a>
      <div class="nav-collapse collapse">
        <ul class="nav navbar-nav">
          <li><a href="{{URL::route('create_album_form')}}">Create New Album</a></li>
        </ul>
      </div><!--/.nav-collapse -->
    </div>
    </div>
    
      <div class="container">
    
        <div class="starter-template">
      
        <div class="row">
          @foreach($albums as $album)
            <div class="col-lg-3">
              <div class="thumbnail" style="min-height: 514px;">
                <img alt="{{$album->name}}" src="/albums/{{$album->cover_image}}">
                <div class="caption">
                  <h3>{{$album->name}}</h3>
                  <p>{{$album->description}}</p>
                  <p>{{count($album->Photos)}} image(s).</p>
                  <p>Created date:  {{ date("d F Y",strtotime($album->created_at)) }} at {{date("g:ha",strtotime($album->created_at)) }}</p>
                  <p><a href="{{URL::route('show_album',%20array('id'=>$album->id))}}" class="btn btn-big btn-default">Show Gallery</a></p>
                </div>
              </div>
            </div>
          @endforeach
        </div>
    
      </div><!-- /.container -->
    </div>
    
  </body>
</html>

Adding a template for creating albums

As you can see in the following code, we prefer to use Twitter’s bootstrap CSS framework. Thisframework allows you to rapidly create useful, responsive, and multi-browser supported interfaces. Next, we need to create a template for creating albums. To create that, save the following code ascreatealbum.blade.php in the resources/views directory:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Create an Album</title>
    <!-- Latest compiled and minified CSS -->
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Latest compiled and minified JavaScript -->
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
  </head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".nav-collapse">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span lclass="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">Awesome Albums</a>
        <div class="nav-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><ahref="{{URL::route('create_album_form')}}">CreateNew Album</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </div>
    <div class="container" style="text-align: center;">
      <div class="span4" style="display: inline-block;margin-top:100px;">

        @if($errors->has())
          <div class="alert alert-block alert-error fade in"id="error-block">
             <?php
             $messages = $errors->all('<li>:message</li>');
            ?>
            <button type="button" class="close"data-dismiss="alert">×</button>
  
            <h4>Warning!</h4>
            <ul>
              @foreach($messages as $message)
                {{$message}}
              @endforeach

            </ul>
          </div>
        @endif

        <form name="createnewalbum" method="POST"action="{{URL::route('create_album')}}"enctype="multipart/form-data">
          <fieldset>
            <legend>Create an Album</legend>
            <div class="form-group">
              <label for="name">Album Name</label>
              <input name="name" type="text" class="form-control"placeholder="Album Name"value="{{Input::old('name')}}">
            </div>
            <div class="form-group">
              <label for="description">Album Description</label>
              <textarea name="description" type="text"class="form-control" placeholder="Albumdescription">{{Input::old('descrption')}}</textarea>
            </div>
            <div class="form-group">
              <label for="cover_image">Select a Cover Image</label>
              {{Form::file('cover_image')}}
            </div>
            <button type="submit" class="btnbtn-default">Create!</button>
          </fieldset>
        </form>
      </div>
    </div> <!-- /container -->
  </body>
</html>

The template creates a basic upload form and shows the validation errors which are passed from thecontroller side. We need just one more template file to list the album images. So, to create it, save the following code as album.blade.php in the  resources/views  directory:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>{{$album->name}}</title>
    <!-- Latest compiled and minified CSS -->
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">

    <!-- Latest compiled and minified JavaScript -->
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
    <style>
      body {
        padding-top: 50px;
      }
      .starter-template {
        padding: 40px 15px;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".nav-collapse">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">Awesome Albums</a>
        <div class="nav-collapse collapse">
          <ul class="nav navbar-nav">
            <li><a href="{{URL::route('create_album_form')}}">Create New Album</a></li>
          </ul>
        </div><!--/.nav-collapse -->
     </div>
    </div>
    <div class="container">
    
      <div class="starter-template">
        <div class="media">
          <img class="media-object pull-left"alt="{{$album->name}}" src="/albums/{{$album->cover_image}}" width="350px">
          <div class="media-body">
            <h2 class="media-heading" style="font-size: 26px;">Album Name:</h2>
            <p>{{$album->name}}</p>
          <div class="media">
          <h2 class="media-heading" style="font-size: 26px;">AlbumDescription :</h2>
          <p>{{$album->description}}<p>
          <a href="{{URL::route('add_image',array('id'=>$album->id))}}"><button type="button"class="btn btn-primary btn-large">Add New Image to Album</button></a>
          <a href="{{URL::route('delete_album',array('id'=>$album->id))}}" onclick="return confirm('Are yousure?')"><button type="button"class="btn btn-danger btn-large">Delete Album</button></a>
        </div>
      </div>
    </div>
    </div>
      <div class="row">
        @foreach($album->Photos as $photo)
          <div class="col-lg-3">
            <div class="thumbnail" style="max-height: 350px;min-height: 350px;">
              <img alt="{{$album->name}}" src="/albums/{{$photo->image}}">
              <div class="caption">
                <p>{{$photo->description}}</p>
                <p><p>Created date:  {{ date("d F Y",strtotime($photo->created_at)) }} at {{ date("g:ha",strtotime($photo->created_at)) }}</p></p>
                <a href="{{URL::route('delete_image',array('id'=>$photo->id))}}" onclick="return confirm('Are you sure?')"><button type="button" class="btnbtn-danger btn-small">Delete Image </button></a>
              </div>
            </div>
          </div>
        @endforeach
      </div>
    </div>

  </body>
</html>

As you may remember, we have used the hasMany() Eloquent method on our model side. On the controller side, we use the function as follows:

$albums = Album::with('Photos')->get();

The code fetches the whole image data in an array that belongs to the album. Because of that, we use the foreach loop in the following template:

@foreach($album->Photos as $photo)
  <div class="col-lg-3">
    <div class="thumbnail" style="max-height: 350px;min-height: 350px;">
    <img alt="{{$album->name}}" src="/albums/{{$photo->image}}">
      <div class="caption">
        <p>{{$photo->description}}</p>
        <p><p>Created date:  {{ date("d F Y",strtotime($photo->created_at)) }} at {{ date("g:ha",strtotime($photo->created_at)) }}</p></p>
        <a href="{{URL::route('delete_image',array('id'=>$photo->id))}}" onclick="return confirm('Are yousure?')"><button type="button" class="btnbtn-danger btn-small">Delete Image</button></a>
      </div>
    </div>
  </div>
@endforeach

 

 

Now we need to create a photo upload form. We’ll upload the photos and assign them to the albums. Let’s first set the routes; open the routes.php file in the app folder and add the following code:

Route::get('/addimage/{id}', array('as' => 'add_image','uses' => 'ImagesController@getForm'));
Route::post('/addimage', array('as' => 'add_image_to_album','uses' => 'ImagesController@postAdd'));
Route::get('/deleteimage/{id}', array('as' => 'delete_image','uses' => 'ImagesController@getDelete'));

We need a template for the photo upload form. To create that, save the following code asaddimage.blade.php in the app/views/ directory:

<!doctype html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Laravel PHP Framework</title>
    <!-- Latest compiled and minified CSS -->
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Latest compiled and minified JavaScript -->
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
  </head>
  <body>
    
    <div class="container" style="text-align: center;">
      <div class="span4" style="display: inline-block;margin-top:100px;">
        @if($errors->has())
          <div class="alert alert-block alert-error fade in"id="error-block">
            <?php
            $messages = $errors->all('<li>:message</li>');
            ?>
            <button type="button" class="close"data-dismiss="alert">×</button>
            
            <h4>Warning!</h4>
            <ul>
              @foreach($messages as $message)
                {{$message}}
              @endforeach
            
            </ul>
          </div>
        @endif
        <form name="addimagetoalbum" method="POST"action="{{URL::route('add_image_to_album')}}"enctype="multipart/form-data">
          <input type="hidden" name="album_id"value="{{$album->id}}" />
          <fieldset>
            <legend>Add an Image to {{$album->name}}</legend>
            <div class="form-group">
              <label for="description">Image Description</label>
              <textarea name="description" type="text"class="form-control" placeholder="Imagedescription"></textarea>
            </div>
            <div class="form-group">
              <label for="image">Select an Image</label>
              {{Form::file('image')}}
            </div>
            <button type="submit" class="btnbtn-default">Add Image!</button>
          </fieldset>
        </form>
      </div>
    </div> <!-- /container -->
  </body>
</html>

Before creating the template, we need to code our controller. So, save the following code asImageController.php in the app/controllers/ directory:

<?php
class ImagesController extends BaseController{

  public function getForm($id)
  {
    $album = Album::find($id);
    return View::make('addimage')
    ->with('album',$album);
  }

  public function postAdd()
  {
    $rules = array(

      'album_id' => 'required|numeric|exists:albums,id',
      'image'=>'required|image'

    );
    
    $validator = Validator::make(Input::all(), $rules);
    if($validator->fails()){

      return Redirect::route('add_image',array('id' =>Input::get('album_id')))
      ->withErrors($validator)
      ->withInput();
    }

    $file = Input::file('image');
    $random_name = str_random(8);
    $destinationPath = 'albums/';
    $extension = $file->getClientOriginalExtension();
    $filename=$random_name.'_album_image.'.$extension;
    $uploadSuccess = Input::file('image')->move($destinationPath, $filename);
    Image::create(array(
      'description' => Input::get('description'),
      'image' => $filename,
      'album_id'=> Input::get('album_id')
    ));

    return Redirect::route('show_album',array('id'=>Input::get('album_id')));
  }
  public function getDelete($id)
  {
    $image = Image::find($id);
    $image->delete();
    return Redirect::route('show_album',array('id'=>$image->album_id));
  }
}

The controller has three functions; the first one is the getForm() function. This function basicallyshows our photo upload form. The second one validates and inserts the data into the database. We’ll explain the validating and inserting functions in the next section. The third one is the getDelete()function. This function basically deletes the image records from the database.

Validating the photo

Laravel has a powerful validation library, which has been mentioned in this book many times. We validate the data in controllers as follows:

$rules = array(

  'album_id' => 'required|numeric|exists:albums,id',
  'image'=>'required|image'

);

$validator = Validator::make(Input::all(), $rules);
if($validator->fails()){

  return Redirect::route('add_image',array('id' =>Input::get('album_id')))
  ->withErrors($validator)
  ->withInput();
}

Let’s examine the code. We defined some rules in array. We have two validation rules in the rulesarray. The first rule is as follows:

'album_id' => 'required|numeric|exists:albums,id'

The preceding rule means that the album_id field is required (must be posted in the form), it must be a numeric value, and must exist in the id column of the albums table as we want to assign images toalbums. The second rule is as follows:

'image'=>'required|image'

The preceding rule means that the image field is required (must be posted in the form) and its content must be an image. Then we check the posted form data using the following code:

$validator = Validator::make(Input::all(), $rules);

The validation function needs two variables. The first one is the data that we need to validate. In this case, we set that using the Input::all() method, which means we need to validate the posted form data. The second one is the rules variable. The rules variable must be set as an array as shown in the following code:

$rules = array(

  'album_id' => 'required|numeric|exists:albums,id',
  'image'=>'required|image'

);

Laravel’s validation class comes with many predefined rules. You can see the updated list of all available validation rules at http://laravel.com/docs/validation#available-validation-rules.

Sometimes, we need to validate only specific MIME types, such as JPEG, BMP, ORG, and PNG. You can easily set the validation rule for this kind of validation as shown in the following code:

'image' =>'required|mimes:jpeg,bmp,png'

Then we check the validation process using the following code:

if($validator->fails()){

  return Redirect::route('add_image',array('id' =>Input::get('album_id')))
  ->withErrors($validator)
  ->withInput();
}

If validation fails, we redirect the browser to the image upload form. Then, we show the rules in the template file using the following code:

@if($errors->has())
  <div class="alert alert-block alert-error fade in"id="error-block">
    <?php
    $messages = $errors->all('<li>:message</li>');
    ?>
    <button type="button" class="close"data-dismiss="alert">×</button>
    
    <h4>Warning!</h4>
    <ul>
      @foreach($messages as $message)
        {{$message}}
      @endforeach

    </ul>
  </div>
@endif

Assigning a photo to an album

The postAdd( ) function is used for processing the request to create a new image record in the database. We get the author’s ID using the following previously mentioned method:

Auth::user()->id

Using the following method, we assign the current user with the blog post. We have a new method in the query as shown in the following code:

Posts::with('Author')->…

We’ve defined a public Photos () function in our Album model using the following code:

public function Photos(){

  return $this->hasMany('images','album_id');
}

The hasMany() method is an Eloquent function for creating relations between tables. Basically, the function has one required variable and one optional variable. The first variable (required) is for defining the target model. The second, which is the optional variable, is for defining the source column of the current model’s table. In this case, we store the albums’ IDs in the album_id column in theimages table. Because of that, we need to define the second variable as album_id in the function. The second parameter is only required if your ID doesn’t follow the convention. Using this method, we can pass our albums’ information and assigned images’ data to the template at the same time.

ALet’s have a quick look at the image-listing section of code in our template file, which is located in resources/views/ album.blade.php:

@foreach($album->Photos as $photo)

  <div class="col-lg-3">
    <div class="thumbnail" style="max-height: 350px;min-height: 350px;">
    <img alt="{{$album->name}}" src="/albums/{{$photo->image}}">
      <div class="caption">
        <p>{{$photo->description}}</p>
        <p><p>Created date:  {{ date("d F Y",strtotime($photo->created_at)) }} at {{ date("g:ha",strtotime($photo->created_at)) }}</p></p>
        <a href="{{URL::route('delete_image',array('id'=>$photo->id))}}" onclick="return confirm('Are yousure?')"><button type="button" class="btnbtn-danger btn-small">Delete Image</button></a>
      </div>
    </div>
  </div>

@endforeach

Moving photos between albums

Moving photos between albums is a great feature for managing the album’s images. Many photo gallery systems come with this feature. So, we can code it easily with Laravel. We need a form and controller function for adding this feature to our photo gallery system. Let’s code the controller function first. Open the ImagesController.php file which is located in app/http/controllers/ and add the following code in it:

public function postMove()
{
  $rules = array(

    'new_album' => 'required|numeric|exists:albums,id',
    'photo'=>'required|numeric|exists:images,id'

  );

  $validator = Validator::make(Input::all(), $rules);
  if($validator->fails()){

    return Redirect::route('index');
  }
  $image = Image::find(Input::get('photo'));
  $image->album_id = Input::get('new_album');
  $image->save();
  return Redirect::route('show_album',array('id'=>Input::get('new_album')));
}

As you can see in the preceding code, we use the Validation class again. Let’s examine the rules. The first rule is as follows:

'new_album' => 'required|numeric|exists:albums,id'

The preceding rule means that the new_album field is required (must be posted in the form), must be a numeric value, and exist in the id column of the albums table. We want to assign images to albums, so the images must exist. The second rule is as follows:

'photo'=>'required|numeric|exists:images,id'

The preceding rule means that the photo field is required (must be posted in the form), must be a numeric value, and exist in the id column of the images table.

After successful validation, we update the album_id column of the photos field and redirect the browser to show the new album of photos using the following code:

$image = Image::find(Input::get('photo'));
$image->album_id = Input::get('new_album');
$image->save();
return Redirect::route('show_album',array('id'=>Input::get('new_album')));

The final code of the Images controller should be as follows:

<?php

class ImagesController extends BaseController{

  public function getForm($id)
  {
    $album = Album::find($id);

    return View::make('addimage')
    ->with('album',$album);
  }

  public function postAdd()
  {
    $rules = array(

      'album_id' => 'required|numeric|exists:albums,id',
      'image'=>'required|image'

    );

    $validator = Validator::make(Input::all(), $rules);
    if($validator->fails()){

      return Redirect::route('add_image',array('id' =>Input::get('album_id')))
      ->withErrors($validator)
      ->withInput();
    }

    $file = Input::file('image');
    $random_name = str_random(8);
    $destinationPath = 'albums/';
    $extension = $file->getClientOriginalExtension();
    $filename=$random_name.'_album_image.'.$extension;
    $uploadSuccess = Input::file('image')->move($destinationPath, $filename);
    Image::create(array(
      'description' => Input::get('description'),
      'image' => $filename,
      'album_id'=> Input::get('album_id')
    ));

    return Redirect::route('show_album',array('id'=>Input::get('album_id')));
  }
  public function getDelete($id)
  {
    $image = Image::find($id);

    $image->delete();
    
    return Redirect::route('show_album',array('id'=>$image->album_id));
  }
  public function postMove()
  {
    $rules = array(
      'new_album' => 'required|numeric|exists:albums,id',
      'photo'=>'required|numeric|exists:images,id'
    );
    $validator = Validator::make(Input::all(), $rules);
    if($validator->fails()){
    
      return Redirect::route('index');
    }
    $image = Image::find(Input::get('photo'));
    $image->album_id = Input::get('new_album');
    $image->save();
    return Redirect::route('show_album',array('id'=>Input::get('new_album')));
  }
}

Our controller is ready, so we need to set up the updated form’s route in app/routes.php. Open the file and add the following code:

Route::post('/moveimage', array('as' => 'move_image', 'uses' => 'ImagesController@postMove'));

The final code located in app/http/routes.php should look as follows:

<?php
Route::get('/', array('as' => 'index', 'uses' =>
  'AlbumsController@getList'));
Route::get('/createalbum', array('as' => 'create_album_form',
  'uses' => 'AlbumsController@getForm'));
Route::post('/createalbum', array('as' => 'create_album',
  'uses' => 'AlbumsController@postCreate'));
Route::get('/deletealbum/{id}', array('as' => 'delete_album',
  'uses' => 'AlbumsController@getDelete'));
Route::get('/album/{id}', array('as' => 'show_album', 'uses' =>
  'AlbumsController@getAlbum'));
Route::get('/addimage/{id}', array('as' => 'add_image', 'uses' =>
  'ImagesController@getForm'));
Route::post('/addimage', array('as' => 'add_image_to_album',
  'uses' => 'ImagesController@postAdd'));
Route::get('/deleteimage/{id}', array('as' => 'delete_image',
'uses' => 'ImagesController@getDelete'));
Route::post('/moveimage', array('as' => 'move_image',
'uses' => 'ImagesController@postMove'));

Creating an update form

Now we need to create our update form in our template file. Open the template file which is located inapp/views/ album.blade.php and change the foreach loop as follows:

@foreach($album->Photos as $photo)
  <div class="col-lg-3">
    <div class="thumbnail" style="max-height: 350px;min-height: 350px;">
      <img alt="{{$album->name}}" src="/albums/{{$photo->image}}">
      <div class="caption">
        <p>{{$photo->description}}</p>
        <p>Created date:  {{ date("d F Y",strtotime($photo->created_at)) }}at {{ date("g:ha",strtotime($photo->created_at)) }}</p>
        <a href="{{URL::route('delete_image',array('id'=>$photo->id))}}" onclick="returnconfirm('Are you sure?')"><button type="button"class="btn btn-danger btn-small">Delete Image</button></a>
        <p>Move image to another Album :</p>
        <form name="movephoto" method="POST"action="{{URL::route('move_image')}}">
          <select name="new_album">
            @foreach($albums as $others)
              <option value="{{$others->id}}">{{$others->name}}</option>
            @endforeach
          </select>
          <input type="hidden" name="photo"value="{{$photo->id}}" />
          <button type="submit" class="btn btn-smallbtn-info" onclick="return confirm('Are you sure?')">Move Image</button>
        </form>
      </div>
    </div>
  </div>
@endforeach

 

Wrapping up

In this article, we’ve created a simple photo gallery system with Laravel’s built-in functions and the Eloquent database driver. We’ve learned how to validate the data, and about the powerful data relation method in Eloquent named hasMany. In the next coming articles , we’ll learn to work with more complex tables, and relational data and relation types.

 

hope you will like it :)