How to setup phpMyAdmin in Homestead

Writing this for my own reference. Hope it helps someone too.

Install using Ubuntu repository:

This will install phpMyAdmin from Ubuntu’s repositories. Assuming that your projects live in /home/vagrant/Code:

  1. On your hosting machine, open a terminal, go to your homestead folder, after “vagrant up”, enter “vagrant ssh” to ssh to your virtual machine.
  2. Then enter “sudo apt-get install phpmyadmin” (Do not select apache2 nor lighttpd when prompted. Just hit tab and enter.)
  3. You will be prompted whether to set up a database, select “OK” and when prompted for password, enter “secret”. (This is the default password for Homestead’s MySQL.)
  4. Now enter “sudo ln -s /usr/share/phpmyadmin/ /home/vagrant/Code/phpmyadmin”
  5. Finally, enter “cd ~/Code && serve phpmyadmin.app /home/vagrant/Code/phpmyadmin”

For Mac/Linux users, follow the following step to add a nice URL for phpMyAdmin.

  1. On your hosting machine, edit the /etc/hosts file with the command “sudo nano /etc/hosts”. I like the nano editor, feel free to use any editor.
  2. Add the following (you can use your virtual machine’s IP address instead of 127.0.0.1):
    • 127.0.0.1 phpmyadmin.app
  3. If you’re using nano editor, press keys Control + o (letter o) to save the changes. Then press keys Control + x to exit the editor.
  4. Now open a browser.
  5. Go to http://phpmyadmin.app
  6. That’s it!

Reference:

http://stackoverflow.com/questions/23788096/how-to-setup-phpmyadmin-on-a-laravel-homestead-box

Laravel Logo

Laravel tip: enqueue script to end of body

UPDATE (7 Jan 2016): Correction to the blade @append. It doesn’t always work if you have more than a few templates appending to the section.

And so I’ve a partial blade template that contains a custom selection form, which will then be used by multiple other (page layout) templates. This allows me to change the design of the custom selection form at any time in a single location rather than going through multiple templates.

The problem is that I’ve written some JavaScript to manipulate the custom selection form on load and on click. Naturally (or lazily), the script depends on jQuery, which will only be loaded at the end of the body.

I don’t want to separate the script from my partial blade template just to insert it into the page layout templates.

Therefore, I need the custom script to be enqueued to the end, after the jQuery has loaded. I thought I could write a class that does something like WordPress’ wp_enqueue_script.

To my horror… er… surprise, Laravel’s Blade template has already catered to this kind of work using:

@section('script')
    @parent
    {{-- something --}}
@endsection

This may sound complicated, but I have 3 hierarchy of blade templates:

  • Master layout template
    • (Page layout) Single column template
      • Partial Form template
    • (Page layout) Two columns template
      • Partial Form template
  1. Master layout template
    • First I create a general master layout that defines 2 main sections: content and script.
    • <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>Laravel 101 Template</title>
          							<link href="css/style.min.css" rel="stylesheet">
        </head>
        <body>
          @yield('content')
      
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
          @section('script')
          @show
        </body>
      </html>
  2. Page layout templates
    • Then I create various page layout templates that inherit from Master layout template.
    • It uses the partial form template to collect user input.
    • @extends('layouts.master')
      
      @section('content')
      
          @include('partials.selection-form')
      
      @stop
  3. Partial form template
    • In the partial form, I can easily append my script to the end of the Master layout template like this:
    • <div class="custom-form">
          <h3>A title</h3>
          <input value="0">
          <ul>
              <li><a href="1">Selection 1</a></li>
              <li><a href="2">Selection 2</a></li>
          </ul>
      </div>
      
      @section('script')
          @parent
          <script>
              $('.custom-form').each(function(){
                  doSomething();
              });
          </script>
      @endsection

That’s it! So simple. Love Laravel.

 

Other consideration which failed to work for me.

window.onload

I’ve tried using JavaScript’s window.onload to wait for all the resources to finish downloading before executing my script. However, due to the multiple partial templates I use within a page layout, the result varies from time to time. It seems that only the first window.onload runs properly. I cannot risk that some users may see what other users may not. It’s probably not designed to work like that.

Redmin Series Part 1: Building a Blog with RedminPortal

redminportal_logo_v2_smIn a series of tutorial, I’ll be showing you the steps of building a blog, a Podcast membership site, and an online shop for selling products, using the backend CMS RedminPortal.

RedminPortal is a Laravel 5 package as a flexible content management system for developers.

 

Prerequisite: This tutorial requires knowledge of HTML, PHP, Twitter Bootstrap and Laravel framework. It is meant for developers working on front end site. We’ll use Laravel 5.1 as the foundation due to its LTS support.

All the source codes will be uploaded to github project https://github.com/redooor/redminstore which will, by the end of this series, be a full front-end store system with RedminPortal. You can then use it as a base to build your own application.

In this first tutorial, we’ll be building a simple blog system (something like WordPress) as a Laravel package, using RedminPortal as the backend CMS.

You’ll learn how to:

  1. Build a Laravel package from scratch
  2. Create controller in Laravel
  3. Use Laravel’s Blade templating engine
  4. Use Laravel’s Localization
  5. Use RedminPortal to generate pages and posts
  6. Create dynamic routes to pages and posts
  7. Catch and show error messages such as HTTP 404

Installation

Laravel 5.1

You need to install a full Laravel 5.1 application. Follow the steps in this link and make sure you have a working local environment before proceeding. You should at least be able to access the home page of the application.

RedminPortal

Next, follow the steps in this link to install RedminPortal to your Laravel application. Take note that some features may not be available in version 0.3 yet. To use the latest (but unstable) version, change this line in composer.json:

"require": {
 "laravel/framework": "5.1.*",
 "redooor/redminportal": "0.3.*"
},

To

"require": {
 "laravel/framework": "5.1.*",
 "redooor/redminportal": "dev-develop"
},

Once you’ve installed, follow the migration and publishing steps to create the admin account. Make sure you can access the admin page and log in using the default admin account.

Laravel Package Creation

Folder Structure

Assuming you have your Laravel application installed inside a folder named “laravelapp”, let’s create a folder structure like this:

laravelapp
|- app
|- bootstrap
|- config
  /* other laravel folders omitted for brevity */
  /* create the following folder and its subfolders */
|- packages
  |- redooor
    |- redminstore
      |- src
        |- App
          |- Http
            |- Controllers
          |- Models
            |- UI
        |- facades
        |- resources
          |- lang
          |- views

Noticed that I have named the folders redooor/redminstore. We’ll be calling this package “Redminstore”. If you decide to rename it to something else, do take note that you need to change all references of “Redminstore” to your new name.

Also, because we’ll be using PSR-4 autoloading in our package, all folders inside folder “src” must stay the same. Otherwise the package will fail to work. Take note of the letter casing too. In Linux (most hosting providers use Linux) and Mac OS, the letter casing will affect autoloading.

I’ve left out the steps for setting up PHPUnit tests. If you’re interested, you may visit the github project to study how I did it.

From this point onwards, when I mention <package>, I’m referring to the path “laravelapp/packages/redooor/redminstore”.

Service Provider

Create a new file inside “<package>/src” folder, name it “RedminstoreServiceProvider.php” with this content:

<?php namespace Redooor\Redminstore;

use Illuminate\Support\ServiceProvider;

class RedminstoreServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        // Get routes
        include __DIR__.'/App/Http/routes.php';
        
        // Get views
        $this->loadViewsFrom(__DIR__.'/resources/views', 'redminstore');
        
        // Establish Translator Namespace
        $this->loadTranslationsFrom(__DIR__.'/resources/lang', 'redminstore');
        
        // Allow end users to publish and modify views
        $this->publishes([
            __DIR__.'/resources/views' => base_path('resources/views/vendor/redooor/redminstore'),
        ]);
        
        // Allow end users to publish and modify public assets
        $this->publishes([
            __DIR__.'/public' => public_path('vendor/redooor/redminstore'),
        ], 'public');
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        // Load autoload for package development environment only
        $autoloader = __DIR__ . '/../vendor/autoload.php';
        if (file_exists($autoloader)) {
            require_once $autoloader;
        }
        
        $this->app->register('Orchestra\Imagine\ImagineServiceProvider');
        
        $this->app->booting(function() {
            $loader = \Illuminate\Foundation\AliasLoader::getInstance();
            $loader->alias('Redminstore', 'Redooor\Redminstore\Facades\Redminstore');
            $loader->alias('Redminportal', 'Redooor\Redminportal\Facades\Redminportal');
            $loader->alias('Imagine', 'Orchestra\Imagine\Facade');
        });
    }
}

Package Facade

Create a new file inside “<package>/src/facades” folder, name it “Redminstore.php” with this content:

<?php namespace Redooor\Redminstore\Facades;

use Illuminate\Support\Facades\Facade;

class Redminstore extends Facade {

    /**
    * Get the registered name of the component.
    *
    * @return string
    */
    protected static function getFacadeAccessor() { return 'redminstore'; }

}

Routes

Create a new file inside “<package>/src/App/Http” folder, name it “routes.php” with blank content for now.

Base Controller

Create a new file inside “<package>/src/App/Http/Controllers” folder, name it “Controller.php” with this content:

<?php namespace Redooor\Redminstore\App\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;

abstract class Controller extends BaseController {

	use DispatchesCommands, ValidatesRequests;

}

Package.json

This file contains the information of our package. It’s used when using with Composer and package repository such as Packagist.org.

Create a new file inside “<package>” folder and name it “package.json” with this content:

{
  "name": "redminstore",
  "version": "0.1.0",
  "description": "RedminStore is a frontend theme for Ecommerce sites using RedminPortal as backend.",
  "keywords": [
    "package",
    "laravel",
    "redooor",
    "redmin",
    "portal",
    "ecommerce",
    "frontend",
    "store"
  ],
  "repository": "https://github.com/redooor/redminstore",
  "homepage": "https://github.com/redooor/redminstore",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Redooor LLP",
  "license": "MIT",
  "engines": {
    "node": ">= 0.10.1"
  },
  "devDependencies": {
    "bower": "^1.7.1",
    "grunt": "^0.4.5",
    "grunt-autoprefixer": "^2.2.0",
    "grunt-banner": "^0.2.3",
    "grunt-contrib-clean": "^0.6.0",
    "grunt-contrib-concat": "^0.5.0",
    "grunt-contrib-connect": "^0.9.0",
    "grunt-contrib-copy": "^0.7.0",
    "grunt-contrib-cssmin": "^0.10.0",
    "grunt-contrib-less": "^0.12.0",
    "grunt-contrib-uglify": "^0.6.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-exec": "^0.4.6"
  }
}

You may ignore those lines within devDependencies for now. They are meant for the full package development and not required in this tutorial.

Composer Setup

Create a new file inside “<package>” folder and name it “composer.json” with this content:

{
    "name": "redooor/redminstore",
    "description": "RedminStore is a frontend theme for Ecommerce sites using RedminPortal as backend.",
    "keywords": ["package", "laravel", "redooor", "redmin", "portal", "ecommerce", "frontend", "store"],
    "license": "MIT",
    "authors": [
        {
            "name": "Redooor LLP",
            "email": "support@redooor.com",
            "homepage": "http://www.redooor.com"
        }
    ],
    "require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.1.*",
		"illuminate/html": "~5.0",
        "doctrine/dbal": "~2.4",
        "orchestra/imagine": "3.1.*",
        "phansys/getid3": "2.1.*@dev",
        "redooor/redminportal": "dev-develop"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.0",
		"mockery/mockery": "0.9.*",
        "orchestra/testbench": "3.1.*"
	},
    "autoload": {
        "classmap": [
            "tests/bases",
            "tests/RedminTestCase.php"
        ],
        "psr-4": {
            "Redooor\\Redminstore\\": "src/"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable" : true
}

Once you’ve the composer.json ready, you will need to run composer to install all the dependencies.

Open a Terminal and run the commands:

cd laravelapp/packages/redooor/redminstore
composer install --prefer-dist -vvv --profile

–prefer-dist tells the installer to use distribution source if available.

-vvv is verbose mode, telling the installer to print its actions.

–profile tells the installer to save a cache in the system so next time you run it will be faster.

Visit https://getcomposer.org/ to learn more about Composer.

Root’s Composer.json

Now we need to tell the main application about the location of our newly created package.

Go to “laravelapp/composer.json” and add 1 more line to “autoload” -> “psr-4” section, like this (line 8):

"autoload": {
    "classmap": [
        "database"
    ],
    "psr-4": {
        "App\\": "app/",
        "Redooor\\Redminportal\\": "packages/redooor/redminportal/src",
        "Redooor\\Redminstore\\": "packages/redooor/redminstore/src"
    }
},

Root’s Config App.php

Next, go to “laravelapp/config/app.php” and add 1 more line under “providers” section, like this (line 18):

'providers' => [

    /*
     * Laravel Framework Service Providers...
     */
    Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
    /* omitted for brevity */

    /*
     * Application Service Providers...
     */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,

    Redooor\Redminportal\RedminportalServiceProvider::class,
    Redooor\Redminstore\RedminstoreServiceProvider::class,

],

Run Composer Update

Open a Terminal and run the command inside the root laravalapp folder:

composer update --prefer-dist -vvv --profile

Master Template

Now, we’re almost ready to get into business. Let’s begin with the master template for the entire User Interface of our site. This template will contain the header and footer of the HTML layout, so we don’t have to repeat them in the rest of the pages we create. It will also define the top navigation bar for the site here.

Create a file inside “<package>/src/resources/views/layouts” folder and name it “master.blade.php” with this content:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
        <!--[if lt IE 9]>
        <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
        @section('head')
        <title>RedminStore by Redooor</title>
        @show
    </head>
    <body>
        <header id="header">
            <div class="navbar navbar-default">
                <div class="container">
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                        <a href="{{ URL::to('/') }}" class="navbar-brand">{{ trans('redminstore::menus.brandname') }}</a>
                    </div>
                    <div class="navbar-collapse collapse">
                        <?php $menu_pages = Redooor\Redminstore\App\Models\UI\Menu::getPages(); ?>
                        @if (count($menu_pages) > 0)
                        <ul class="nav navbar-nav">
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ trans('redminstore::menus.pages') }} <span class="caret"></span></a>
                                <ul class="dropdown-menu">
                                    @foreach ($menu_pages as $menu_page)
                                    <li><a href="{{ URL::to('page/' . $menu_page->slug) }}">{{ $menu_page->title }}</a></li>
                                    @endforeach
                                </ul>
                            </li>
                        </ul>
                        @endif
                        <?php $menu_posts = Redooor\Redminstore\App\Models\UI\Menu::getPosts(); ?>
                        @if (count($menu_posts) > 0)
                        <ul class="nav navbar-nav">
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ trans('redminstore::menus.posts') }} <span class="caret"></span></a>
                                <ul class="dropdown-menu">
                                    @foreach ($menu_posts as $menu_post)
                                    <li><a href="{{ URL::to('post/' . $menu_post->slug) }}">{{ $menu_post->title }}</a></li>
                                    @endforeach
                                </ul>
                            </li>
                        </ul>
                        @endif
                        <ul class="nav navbar-nav navbar-right">
                            <li><a href="{{ url('/admin') }}">{{ trans('redminstore::menus.adminlogin') }}</a></li>
                        </ul>
                    </div><!--/.nav-collapse -->

                </div>
            </div>
        </header>

        <div id="main">
            <div class="container">
                @yield('content')
            </div>
        </div><!--End main-->
        
        <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

        @section('footer')
        @show
    </body>
</html>

If you’re new to Laravel’s Blade template, take special attention to all the code beginning with an @ sign. Read here to study more about Blade template.

The HTML is a bare minimum site to get things working with Bootstrap. You may have a different HTML template altogether. But do take note of the following lines.

Line 10 to 12: I’ve created a section “head” that can be overwritten by any child template that inherits from this master template. You will see this in action soon when we create a child template.

Line 27: I’m calling a static function to grab all the public pages from RedminPortal, and then use the returned list of pages to generate the dropdown menu in lines 28 to 39. We don’t have that static function Menu::getPages() yet. Let’s create it soon under section “Create UI Menu”.

Line 28 to 39: This dropdown menu is generated with an if-else statement and a returned list of public pages from static function Menu::getPages(). If the function returns a null, the dropdown menu will not be created. So you will not see this dropdown menu until you create at least 1 page with “Private” option unchecked in RedminPortal.

Line 24 and 41: The curly brackets {{}} is Laravel’s equivalent of PHP’s echo. So whatever is within the curly brackets will be printed on the page. I’m using the helper function trans() here to print the localization text. Let’s create the localization file soon under section “Localization”. Read more here about Laravel’s Localization.

Line 51: This is where the child template gets its content printed. We will see it in action soon when we create child template.

Line 58 to 59: This is another section where child template can insert its own footer into master template. This is useful if you have specific JavaScript that is used in 1 child template only and doesn’t make sense to put it in master template.

Create UI Menu

As mentioned previously, we need to create a static function to retrieve all public pages to return to the master template.

Create a file inside “<package>/src/App/Models/UI” folder and name it “Menu.php” with this content:

<?php namespace Redooor\Redminstore\App\Models\UI;

use Redooor\Redminportal\App\Models\Page;
use Redooor\Redminportal\App\Models\Post;

class Menu
{
    /**
    /* Get all pages.
    /*
    /* @param bool $private To retrieve private or public pages, default public.
    /* @return array(Page) or null if nothing found
     */
    public static function getPages($private = false)
    {
        $pages = Page::where('private', $private)->get();
        
        return $pages;
    }
    
    /**
    /* Get all posts.
    /*
    /* @param bool $private To retrieve private or public posts, default public.
    /* @return array(Post) or null if nothing found
     */
    public static function getPosts($private = false)
    {
        $posts = Post::where('private', $private)->get();
        
        return $posts;
    }
}

We’re using eloquent model Redooor\Redminportal\App\Models\Page from the CMS RedminPortal. Only those pages with flag “private” marked as false will be returned. To study more about Eloquent model in Laravel, read here.

Localization

All localization files must be stored in the folder “<package>/src/resources/lang”. The language are denoted by the subfolder with “en” as the default. For example, if you want to localize to Simplified Chinese, simply add a subfolder name “sc”. Learn more about Laravel’s Localization here.

Now, let’s create a file inside “<package>/src/resources/lang/en” folder and name it “menus.php” with this content:

<?php

return array(
    'brandname' => 'RedminStore',
    'adminlogin' => 'Admin Login',
    'pages' => 'Pages',
    'posts' => 'Posts'
);

In this tutorial, we’ve only created 3 items for “brandname”, “adminlogin” and “pages”.

To use them in any template, simply call the helper function trans() like this:

{{ trans('redminstore::menus.brandname') }}
{{ trans('redminstore::menus.adminlogin') }}
{{ trans('redminstore::menus.pages') }}

The convention for our tutorial will be:

trans('redminstore::menus.<key>')

Why do we use localization in this simple tutorial? Well, I just wanted to show you how you can use localization. But most importantly, if you decide to use RedminStore as the base to build your product, you can always overwrite the localization file without changing the Master Template. Just go to your root laravelapp folder, and add that edited “menus.php” file to the folder “laravelapp/resources/lang/packages/en/redminstore/” and you’re good to go.

Child Templates

Let’s create our first child templates to test our setup.

404 Template

We’ll start with a simple 404 page to tell users that the page they are trying to view cannot be found. This is useful for returning non-existence page later on.

Create a new file inside “<package>/src/resources/views/general” folder and name it “404.blade.php” with this content:

@extends('redminstore::layouts.master')

@section('content')
    <div class='row'>
        <div class='col-md-12'>
            <div class="jumbotron">
                <h1>Oops! 404, Page Not Found.</h1>
                <p>Unfortunately, we're not able to find that URL.</p>
            </div>
        </div>
    </div>
@stop

This is a much shorter template than our master template because we’re extending all layout from layouts.master template.

Line 1: This template extends the content from views/layouts/master.blade.php.

Line 3 and 12: We can now insert our child template’s content into master within this section “content”.

Home Template

This template will be used as the landing page for our site. So when user goes to the root site (e.g. laravelapp.app), we will show this page to them.

Create a new file inside “<package>/src/resources/views/pages” folder and name it “home.blade.php” with this content:

@extends('redminstore::layouts.master')

@section('content')
    <div class='row'>
        <div class='col-md-8'>
            <div class="panel panel-default">
                <div class="panel-body">
                    <h1>RedminStore <small>by Redooor</small></h1>
                    <p>This is a demo template to help you get started with RedminPortal CMS.</p>
                    <p>Try adding a few pages/posts in RedminPortal and watch the page/post being generated automatically.</p>
                </div>
            </div>
        </div>
        <div class='col-md-4'>
            <div class="well well-sm shadow-depth-1">
                <img src="{{ URL::to('vendor/redooor/redminstore/img/redminportal_logo.png') }}" title="ReminPortal" class="img-responsive pull-right">
                <h3>Contribution</h3>
                <p>Source code at <a href="https://github.com/redooor/redminstore">Github</a></p>
                <h3>Maintenance</h3>
                <p>Maintained by the core team with the help of our contributors.</p>
                <h3>License</h3>
                <p>RedminPortal is open-sourced software licensed under the <a href="http://opensource.org/licenses/MIT">MIT license</a>.</p>
            </div>
        </div>
    </div>
@stop

Our First Routes

In previous section, we’ve created 2 templates but there’s no way to see these pages yet. Do view these pages, we need to set up some routes.

If you go to your root site now (e.g. laravelapp.app), you will see the default page with a big Laravel text. That default page comes with every new Laravel installation.

Removing Default Routes

Let’s remove all the default routes from new Laravel installation by deleting the content in “laravalapp/app/Http/routes.php” file.

When you’re done deleting the content, go to your root site again (e.g. laravelapp.app) and you should see an error message saying the route does not exist.

Inserting Our Package Routes

If you look at previously created file RedminstoreServiceProvider.php (Line 15), you would notice that we are loading our routes.php file on boot. This ensures that our package’s routes come after the application’s routes. With that in place, we can save as many routes in our package’s routes.php as we want without changing the root laravelapp’s routes.php.

Custom Routes

That said, we can now edit our package’s routes at “<package>/src/App/Http/routes.php” with this content:

<?php

/*
|--------------------------------------------------------------------------
| Package Routes
|--------------------------------------------------------------------------
*/

Route::group(['namespace' => 'Redooor\Redminstore\App\Http\Controllers'], function () {
    Route::get('/', 'PageController@showHome');
    Route::get('page', 'PageController@show404');
    Route::get('page/{slug}', 'PageController@loadPage');
    Route::get('post', 'PostController@show404');
    Route::get('post/{slug}', 'PostController@loadPost');
});

Line 10: This line replaces the root path (e.g. laravelapp.app) which we had previously deleted from the root’s routes.php. It’s loading PageController’s function showHome().

Line 11: This line points to a new route with “/page” appended after the site path (e.g. laravelapp.app/page). This page serves as an error page when no slug is given. It’s loading PageController’s function show404().

Line 12: This line points to any route with “/page/<slug>” appended after the site path (e.g. laravelapp.app/page/<slug>). This page shows the content of RedminPortal::Page model that matches the given slug. It’s loading PageController’s function loadPage().

These routes will not work yet. We need the PageController to provide the content of the page to the router.

Next, let’s create the PageController.

PageController

This PageController is responsible of returning the content of the page to the router. I’ve introduced 3 functions in the previous section. They are:

  • showHome()
  • show404()
  • loadPage($slug)

Create a new file in “<package>/src/App/Http/Controllers” folder and name it “PageController.php” with this content:

<?php namespace Redooor\Redminstore\App\Http\Controllers;

use Validator;
use Redooor\Redminportal\App\Models\Page;

class PageController extends Controller
{
    public function showHome()
    {
        return view('redminstore::pages.home');
    }
    
    public function show404()
    {
        return view('redminstore::general.404');
    }
    
    public function loadPage($slug)
    {
        $inputs = array('slug' => $slug);
        $rules = array('slug' => 'required|alpha_dash');
        $validation = Validator::make($inputs, $rules);
        
        if ($validation->passes()) {
            $page = Page::where('slug', $slug)->where('private', false)->first();
            
            if ($page) {
                return view('redminstore::pages.view')
                    ->with('page', $page);
            }
        }
        
        return view('redminstore::general.404');
    }
}

Return View

showHome() and show404() simply return the templates we had created earlier on. showHome() return the view from pages.home while show404() return the view from general.404. In these templates, the content are static and we’re not expect any variable inside.

Notice the naming convention we’re using here.

view('redminstore::pages.home')

The package’s name must be included before the folder.file name convention. Without the package’s name, Laravel will attempt to load the files from the root laravelapp resources/views folder instead.

So in Laravel, this

return view('pages.home');
>>> result folder: laravelapp/resources/views/pages/home.blade.php

is different from this

return view('redminstore::pages.home');
>>> result folder: laravelapp/packages/redooor/redminstore/src/resources/views/pages/home.blade.php

Now, try loading the root site (e.g. laravelapp.app) and ‘/page’ site (e.g. laravelapp.app/page). You should see the respective page. If not, revisit all the steps above to ensure all files and folders are in place.

If you try loading the ‘/page/<slug>’ site (e.g. laravelapp.app/page/test-page’), you should see an error message. This is because we have not created the template for pages.view yet.

Create Dynamic Page View

PageController::loadPage($slug) function searches all non-private (i.e. public) pages that have slug matches the given $slug, and then pass the RedminPortal::Page model to the template before returning the view.

The passing of data into the template is this line:

return view('redminstore::pages.view')->with('page', $page);

And now, create a new file in “<package>/src/resources/views/pages” folder and name it “view.blade.php” with this content:

@extends('redminstore::layouts.master')

@section('head')
<title>Redminstore: {{ $page->title }}</title>
@stop

@section('content')
    {!! $page->content !!}
@stop

As you can see, it is a very simple template that extends from layouts.master.

Line 4: Overwrites the <title> of the page with the Page’s title.

Line 8: Outputs the content of the returned Page result. We want the full content (except the navigation bar) to be whatever the blogger has written.

Catching non-existence page

If no Page is found for the given slug, the loadPage($slug) function will return the 404 page to the router.

You can try it by passing any non-existence slug to the site path (e.g. laravelapp.app/page/no-such-page-exist).

Create Page in RedminPortal

Finally, to see our work in action, login to RedminPortal CMS via “/admin” site (e.g. laravelapp.app/admin) with the default credential (if you’re lost, refer to RedminPortal installation guide).

In the menu, go to “Pages” under “Content Management”. Create a few pages with the “Private” flag unchecked.

Then go to root site (e.g. laravelapp.app) and you should see the “Pages” dropdown menu. Open it to select the pages you have created. It should be (almost) What-You-See-Is-What-You-Get.

End of Part 1

“What? How about Post? I thought you said we will learn how to create posts as well?”

Ah, well, in a way, I lied. :p

However, the steps for generating Post is exactly the same as generating Page. It’s just a matter of duplicating and changing the name.

Give it a try and see if you can create your own PostController, view and route.

I’ve uploaded my code in github. If you really can’t get it, study my code in github or drop me a comment below. I’ll try as much as possible to answer your questions.

RedminPortal Developer Guide: How to use Coupon?

In this tutorial, I’ll show you how to apply coupon to your products and calculate the discounted price.

Note: This tutorial refers to the latest develop branch which features may not be in the master branch yet. These features will eventually be ported to develop-v0.2 branch, which supports PHP 5.4.

Overview of RedminPortal

redminportal_logo_v2_sm

RedminPortal is a content management system for developers. It only offers backend system for end users and leave the front end solely to the developer. Hence, in many cases, it is up to the developers to design their business logic for their front end.

Let’s Begin

In this tutorial, I’ll be showing you one way of using the coupon. However, it is up to your imagination and creativity to come up with many other ways. Treat this as a guiding ground but keeping in mind that this is not the only way.

Product creation

From the portal, create as many products as you see fit.Redminportal products created

Coupon Creation

Next, create coupons that are “restricted to” these products. You can either restrict the coupons to Categories, Products, Module/Membership or Bundles.Redminportal coupons created

How to create a new order?

Let’s assume we want to have a Cart class that creates a new order and add products into it. Here’s the code:

use Auth;
use Redooor\Redminportal\App\Models\Order;

class Cart
{
  public $order;

  function __construct()
  {
    $user = Auth::user();

    $this->order = new Order;
    $this->order->user_id = $user->id;
    $this->order->paid = 0;
    $this->order->transaction_id = date('Ymd') . '_order_' . $user->id;
    $this->order->payment_status = 'Pending';
    $this->order->options = ''; //Optional
    $this->order->save();
  }
}

Now, to create a new cart in your code, simply do:

$cart = new Cart;

How to add products to an order?

Most (if not all) of the models in Redminportal use Eloquent relationships. To add any product into an order, you just need to save the Product object into Order object. I’ll create a new function in Cart class for adding product.

class Cart
{
  public $order;
  
  function __construct()
  {
    /* omitted */
  }

  public function addProduct($product_id)
  {
    $product = Product::find($product_id);
    
    if ($product) {
      $this->order->products()->save($product);
    }
    
    return $product; // return null if no product found
  }
}

Notice in line 15 we’re using relationship insertion to add product into order.

How to get the total price of an order?

There’s a method in Order class for you to get the total sum of all the product prices. It’s located at <redminportal>/src/App/Models/Order.php.

In our example, if you want to get the total sum of all the prices in a cart, do this:

$totalprice = $cart->order->getTotalprice();

NOTE: This total price does not include discounted value from coupon. It is purely the summation of the prices of all products/pricelists/bundles.

How to apply a coupon to an order?

Using the same relationship insertion, you can add any coupon into an order like this:

$coupon = Coupon::find($coupon_id);

if ($coupon) {
  $cart->order->coupons()->save($coupon);
}

 

This insertion doesn’t check if the coupon is applicable to any product within the order. It merely adds the coupon to the order.

To add a coupon and making sure that it checks the “multiple_coupons” flag, you can use this method addCoupon():

$coupon = Coupon::find($coupon_id);

if ($coupon) {
  $cart->order->addCoupon($coupon);
}

All other checks are up to you, the developers.

How to get the total discounts in an order?

You can use the method getDiscounts() to get a list of all the products with applicable coupons. And then calculate the discount from there.

Note: the value of each discount is stored in the record key “value”.

I will add this function to the Cart class.

public function getTotaldiscount()
{
  $all_discounts = $this->order->getDiscounts();

  $totalDiscount = 0;

  foreach ($all_discounts as $discount) {
    $totalDiscount += $discount['value'];
  }

  return $totalDiscount;
}

// Alternative, shorter code with Laravel's collection
public function getTotaldiscount()
{
  return collect($order->getDiscounts())->sum('value');
}

You can then calculate the total payable amount like this:

$totalpayable = $cart->order->getTotalprice() - $cart->getTotaldiscount();
Update 14 Dec 2015:

Using the latest develop branch, you can now retrieve the total discount from an order like this:

$cart->order->getTotaldiscount();
Caution:

Take note that getDiscounts() saves all the discount information in the $order->options[‘discounts’] array. This is to reduce recalculation whenever you call this method.

However, when you add or remove any coupon or product from the order, you need to call setDiscounts() method to recalculate the new value. Like this:

/* after adding some products */
/* and then add/remove some coupons */

$cart->order->setDiscounts();

$new_totalpayable = $cart->order->getTotalprice() - $cart->getTotaldiscount();

 

That’s it. Do leave a comment if there’s any mistake and I’ll update the post.

Laravel Logo

Setting up Laravel 5 on shared hosting server

UPDATE (7 Jan 2016): Added a final step for pointing domain to subfolder.

UPDATE (4 Apr 2016): I’ve received a number of enquiries with this setup. Please test it on your local environment first. Use homestead to create a domain with the same folder structure. If it’s working on homestead, it’ll most likely work on your shared server.

UPDATE (15 Jun 2016): Added some common problems found from the lovely comments. Scroll to the bottom to see

In this tutorial, I’ll show you some guide to deploy your Laravel 5 application onto a shared hosting server.

By default, the Laravel folder structure assumed that the public folder is within the application itself. This is the high level folder structure:

your_app_name
|--app
|--bootstrap
|--config
|--database
|--public
  |--assets
  |--index.php  <--we need to point here
|--resources
|--storage
|--tests
|--vendor

In an ideal scenario, you can simply point the web domain to the public folder so that the URL loads public\index.php.

However, in a shared hosting server, you are limited to putting your web serving files in a folder probably named public_html or www which is publicly accessible via web domain. You may be tempted to do this (NOT recommended):

shared_hosting_root_NOT_recommended
|--other_folders (not accessible via web domain)
|--public_html
  |--your_app_name
    |--app
    |--bootstrap
    |--config
    |--database
    |--public
      |--assets
      |--index.php  <--we need to point here
    |--resources
    |--storage
    |--tests
    |--vendor

You can then point your web domain to the public folder.

The problem with this is that your application is now publicly accessible and that may expose vulnerability. You cannot be sure if someone somewhere is able to access your config folder and look at all your sensitive information such as database credentials.

Instead, put your application root into a folder outside of public_html, and place only the public folder within public_html.

This is the recommended folder structure:

shared_hosting_root_recommended
|--other_folders (not accessible via web domain)
|--applications  (not accessible via web domain)
  |--your_app_name
    |--app
      |--GoPublic.php <--we'll create this
    |--bootstrap
    |--config
    |--database
    |--public <--copy content to public_html
      |--assets
      |--index.php
    |--resources
    |--storage
    |--tests
    |--vendor
    |--deploy.sh <--we'll create this
|--public_html
  |--your_app_name
    |--assets
    |--index.php <-- we need to point here

In Laravel 4, you can easily tell your application that the public folder is now in another location by simply changing the file bootstrap/path.php. See point 9 of my Laravel 4 tutorial.

However, in Laravel 5, this file no longer exists. I’ll show you how.

1. Create a class to point to new location

Create a new file shared_hosting_root\applications\your_app_root\app\GoPublic.php

<?php namespace App;

class GoPublic extends \Illuminate\Foundation\Application
{
 /**
 * Get the path to the public / web directory.
 *
 * @return string
 */
 public function publicPath()
 {
 return $this->basePath.DIRECTORY_SEPARATOR.'../../public_html/your_app_name';
 }
}

We’re using relative path here so you don’t have to know the absolute path. But it is extremely crucial to follow the same recommended folder structure I’ve mentioned earlier.

Updated:

You may need to run the following command in your root folder to autoload the new class. Otherwise you will get an error saying the class cannot be found.

composer dump-autoload

2. Edit bootstrap\app.php

Now we need to tell our application the location of the new public folder. Open and edit your_app_name\bootstrap\app.php:

<?php

/* -- remove/comment this original code
$app = new Illuminate\Foundation\Application(
 realpath(__DIR__.'/../')
);
-- until here */

/* -- add this new code -- */
$app = new App\GoPublic(
 realpath(__DIR__.'/../')
);

As you can see above, we’re using the new class that we’ve created in step 1.

3. Edit public\index.php

You need to edit these 2 lines.

From line 21:

require __DIR__.'/../bootstrap/autoload.php';

to

require __DIR__.'/../../applications/your_app_name/bootstrap/autoload.php';

And from line 35:

$app = require_once __DIR__.'/../bootstrap/app.php';

to

$app = require_once __DIR__.'/../../applications/your_app_name/bootstrap/app.php';

4. Copy public to public_html

Now, copy the content inside applications\your_app_name\public into public_html\your_app_name.

To simplify the process, I would create a bash file to automatically copy the files to the new location.

Create a new file shared_hosting_root\applications\your_app_name\deploy.sh

#!/bin/bash
echo "Copy public folder to public_html/your_app_name"
rsync -rv ./public/ ../../public_html/your_app_name

Using rsync ensures that if you ever run this deploy.sh several times, only those that are newer will be copied to the new location.

When you’re ready to run it, in the terminal run:

?> cd applications\your_app_name
?> sh deploy.sh

Caution: You have to run deploy.sh within applications\your_app_name, otherwise it will fail.

5. Accessing your app

There are 2 ways of accessing your application from the hosting server. You need to follow either one of the following step depending on your setup.

5a) Using subdomain

The easier way of accessing your application is to set up a subdomain (e.g. your_app_name.example.com) and point it to your application folder (i.e. public_html/your_app_name). This should automagically work without much hassle.

5b) Using primary domain

The other (slightly more complex) way is to point your primary domain (e.g. example.com) to your application folder (i.e. public_html/your_app_name).

By default, the primary domain points to the folder public_html in most of the shared hosting environment.

To do that, you need to create (or edit if it’s already there) a .htaccess file inside the public_html folder.

# Do not change this line. 
RewriteEngine on 

# Change example.com to your domain name
RewriteCond %{HTTP_HOST} ^(www.)?example.com$ 

# Change your_app_name to the subfolder name
RewriteCond %{REQUEST_URI} !^/your_app_name/ 

# Don't change the following two lines. 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 

# Change your_app_name to the subfolder name
# Change example.com to your domain name
RewriteRule ^(.*)$ /your_app_name/$1 
RewriteCond %{HTTP_HOST} ^(www.)?example.com$ 
RewriteRule ^(/)?$ your_app_name/index.php [L]

6. Em…

And that’s it.

I would also recommend using the same folder structure during your development so that you can experience the whole process of deployment before you upload to your hosting server.

Hope this helps!

7. Common Problems

There were a few cases of the following that may cause your Laravel 5 to not work on your live server:

  1. Your PHP version is lower than 5.5.9. Laravel 5 requires PHP 5.5.9 and above.
  2. Your folder structure is different. Make sure your local test environment is exactly the same as your live server. Starting from your server’s root folder.
  3. Re-read the whole tutorial. I find that some of my codes were truncated, so make sure you copy and paste correctly. Try scrolling left and right to see the full code line. Sorry about that.
Laravel Logo

Laravel migration error Cannot add foreign key contraint

If you’re getting this error message when executing migration on an existing table, you’re probably having the same issue as I was.

[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table `pages` add constraint pages_category_id_foreign foreign key (`category_id`) references `categories` (`id`))
[PDOException]                                                          
  SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint

The problem is because my ‘categories’ table was created as MyISAM type, which doesn’t support foreign key.

The solution is surprisingly very simple: Convert the table to InnoDB type.

So before I create the new table ‘pages’ which reference to the existing table ‘categories’ with a foreign key, I inserted a line to convert the table to InnoDB type.

We can do that easily with Laravel’s DB::statement.

Like this:

// Default type MyISAM doesn't support foreign key
// Convert table to InnoDB
if (Schema::hasTable('categories')) {
    DB::statement('ALTER TABLE categories ENGINE = InnoDB');
}

if (! Schema::hasTable('pages')) {
    Schema::create('pages', function(Blueprint $table)
    {
        $table->increments('id');
        $table->timestamps();
        $table->string('title', 255);
        ... omitted ...
        $table->integer('category_id')->nullable()->unsigned();
        $table->foreign('category_id')->references('id')->on('categories');
    });
}

I was stuck at this for almost half a day trying to figure out why the migration keeps giving me error (although the tables were created nevertheless).

Hope this helps.

Laravel Logo

Laravel: order of migrations with foreign keys

Honestly I didn’t know that SQLite is so forgiving until I had to migrate to MySQL in production recently.

Planning the database schema ahead of implementation was a habit, and it didn’t occur to me that the order of migrations with foreign keys was so important. SQLite didn’t produce any error, but when running Laravel migration in MySQL,  it produced an error like this for tables with foreign keys:

[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL : alter table `profiles` add constraint profiles_company_id_foreign foreign key (`company_id`) references `companies` (`id`))

I was so puzzled because the schema looked correct and it ran without error with SQLite. So it took me awhile to realise that only 2 tables were affected, and coincidentally that 2 tables were the first 2 to be created of all migrations.

The order of the old migrations was like this:

  1. 2014_01_01_00001_create_profiles_table.php
    • Foreign key company_id references to id of table companies
  2. 2014_01_01_00002_create_contacts_table.php
    • Foreign key contacttype_id references to id of table contacttypes
  3. 2014_01_01_00003_create_companies_table.php
  4. 2014_01_01_00004_create_contacttypes_table.php

That first 2 tables referenced to some tables that didn’t exist at the point of migration, that’s why MySQL had an error trying to add a constraint.

I had to change the order of the migrations for that 2 tables to be created without error.

Modified order of migrations:

  1. 2014_01_01_00001_create_companies_table.php
  2. 2014_01_01_00002_create_contacttypes_table.php
  3.  2014_01_01_00003_create_profiles_table.php
    • Foreign key company_id references to id of table companies
  4.  2014_01_01_00004_create_contacts_table.php
    • Foreign key contacttype_id references to id of table contacttypes

Moral of the story: Tables with foreign keys should be created after the tables they reference to have been created.

I hope this will help someone else in the future.

Laravel Logo

Laravel 4: Using query builder lists for Form::select

Recently I realised I’ve been doing the wrong way to populate the array for Form::select().

In order to get Form::select() to work, the array you parse in must contain key => value. So in the past, I’ve been doing it this way:

$select_array = array();
foreach (Classname::all() as $model) {
    $select_array[$model->id] = $model->name;
}

It does the work, but there’s a built-in function in Laravel that can do this (probably faster, I think) in just 1 line:

$select_array = Classname::lists('name', 'id');

It returns an array that will work with Form::select().

This function is mentioned in the Laravel Documentation to retrieve specific column from a model. But it wasn’t clear that it’s also useful to populate an array for Form::select().

Advanced level

What if the value you want to get is in another class that’s related to this class? Well, we can combine join() with select() and then list().

For example:

$select_array = ModelOne::join('modeltwos', 'modeltwos.id', '=', 'modelones.modeltwo_id')
 ->select('modeltwos.name', 'modelones.id')
 ->lists('name', 'id');

I hope this is useful. Do leave a comment if you have a better way of populating the array for Form::select().

Laravel Logo

How to override config files of Laravel 4 package

If you want to be able to allow the users who use your package to publish and modify the package’s config files, you’ll need to add these into your Server Provider file, inside function register().

For example, my config files are structured like this:

- vendorname
  - packagename
    - src
      - config
        - image.php
        - config.php
        - menu.php

So within the PackagenameServiceProvider.php file, add these:

public function register()
{
  // other code omitted...

  // Get config loader
  $loader = $this->app['config']->getLoader();

  // Get environment name
  $env = $this->app['config']->getEnvironment();

  // Add package namespace with path set, override package if app config exists in the main app directory
 if (file_exists(app_path() .'/config/packages/vendorname/packagename')) {
 $loader->addNamespace('redminhrportal', app_path() .'/config/packages/vendorname/packagename');
 } else {
 $loader->addNamespace('packagename',__DIR__.'/../../config');
 }

  // Load package override config file
  $menu = $loader->load($env,'menu','packagename');
  $image = $loader->load($env,'image','packagename');
  $config = $loader->load($env,'config','packagename');

  // Override value
  $this->app['config']->set('packagename::menu',$menu);
  $this->app['config']->set('packagename::image',$image);
  $this->app['config']->set('packagename::config',$config);
}

To override the configuration files, users will have to publish the config file with the command below:

php artisan config:publish vendorname/packagename

Htaccess redirect for Laravel 4

Needed to redirect all non-www to www domain (for Google SEO purpose) but was having problem with the routing in Laravel.

Found this to work perfectly.

<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On

RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

</IfModule>

Source: http://stackoverflow.com/a/19839609