Getting started

  1. Prerequisites

    • PHP7
      • cli
      • xml
    • composer
    • git

    Easy install in Ubuntu 18.04:

     sudo apt install php php-cli php-xml composer git
    
  2. Download project base, go to project directory and optionally remove .git directory:

     git clone https://github.com/kehikko/root <YOUR-PROJECT-NAME>
     cd <YOUR-PROJECT-NAME>
     rm -rf .git
    
  3. Install basic depencies using composer:

     composer update
    
  4. Setup asset links:

     ./kehikko assets:update
    
  5. Run the site as a development version using PHP’s built-in web server:

     php -S localhost:8000 web/app.php
    

    Then checkout http://localhost:8000.

  6. To make the basic site work with apache, copy example htaccess to web directory:

     cp config/htaccess.default web/.htaccess
    

    Of course you need to setup apache (or any other web server) for this to work and that will not be covered here.

Basic account and authorization

Install basic account handling packages:

composer require kehikko/account
composer require kehikko/route-account
composer require kehikko/route-admin

Enable authenticator by adding following under modules in config/config.yml:

# define module configurations and sometimes classes to be autoloaded
modules:
  # authentication and account handling
  Core\Session:
    timeout: 1800
    authenticators:
      - Account\Yaml\Account

Enable routing by prepending following to config/route.yml:

admin:
  pattern: /admin
account:
  pattern: /account

Update asset links:

./kehikko assets:update

Add first admin user (this might complain about missing file config/users.yml before creating it):

./kehikko account:user -c -r role:admin -p password username

Switch to navigation that includes account stuff by modifying views/base.html to this:

  <body>
    {% block navigation %}
    {% include 'navigation-account.html' %}
    {% endblock %}

Now you should be able to login with given username and password and see some tools under Account-menu.

Filesystem structure

Base system:

project-root/
    config/
        cli-config.php # doctrine helper, if doctrine is used
        config.yaml # main configuration
        route.yaml # main routes
        translations.yaml # main translations
    routes/
        common/ # single route
            public/ # public files 
                css/
                js/
            views/ # views (templates) to be rendered
                400.html
                403.html
                404.html
                500.html
            config.yml # route configuration, usually contains "javascript:" and "css:" lists
            route.yml # subroutes under this route
            translations.yml # translations to be merged with main translations
    vendor/ # contains external components installed by composer
        ...
    views/ # base views for the site
        base.html # bootstrap based "normal" view
        base-wide.html # bootstrap based wide view
        css.html # included usually from base*.html to insert css
        error-exception.html # exception view for serious errors
        javascript.html # included usually from base*.html to insert javascript
        messages.html # simple layout to view messages system to user
        navigation.html # basic navigation
        navigation-account.html # basic navigation with account menu
    web/ # public (static) files
        app.php # router
        css/
            site.css
        common -> ../routes/common/public # link to public files for a specific route
        js/
            site.js
        images/
            ...
    kehikko # command line tool to do different system tasks

Larger example

Creating a new module

Now you should have a simple model class in modules/GoodestCode/GC.php:

<?php

namespace GoodestCode;

class GC extends \Core\Module
{
    public function who(string $name)
    {
        return 'Who is the goodest coder? Who? You are, ' . $name . ', you are!';
    }
}

Creating a new controller

Now you should have a simple controller class in routes/gc/controllers/GooderCodeController.php:

<?php

class GooderCodeController extends \Core\Controller
{
    public function whoAction(string $name)
    {
        $gc = new \GoodestCode\GC();

        $params = array('who' => $gc->who($name));

        return $this->render('goodcode.html', $params);
    }
}

Creating V from MVC

This is how the template could look like:

<h4></h4>

Setting up routing

Now we need to set up routing so it knows how to serve the page we just created. This is pretty simple in current case, so this description will be short.

Add base route to file config/route-local.yml (create this file if it does not exist):

gc: # this is the directory under routes
  pattern: /goodcode # this is the base url for this route

Add subroute to file routes/gc/route.yml (create this file if it does not exist):

who: # this is for pointing to this route elsewhere from the code
  pattern: /who/{name} # this is the url after base url
  controller: GooderCode # guess what this means
  action: who # and where this leads

Difference between route-local.yml and route.yml in both locations is that the local one should not be added to version control (git) and the other should. So changes in the local one are just local to your own development environment and the changes in the other are distributed to everyone. Using one or another depends highly on the use-case. Testing should be always done in the local one of course so it does not mess up example production servers!

Now you should be able to see the page in your development host: http://YOUR-DEV-HOST/goodcode/who/NAME-PARAM/

Some extended twig usage

Add new action to the previous controller, add new subroute pointing to it and create new view for it:

Action:

    public function whosAction()
    {
        $params = array();
        $params['names'] = array('ville', 'jaakko', 'pekka', 'sami');
        return $this->render('goodcodes.html', $params);
    }

Add to routes/gc/route.yml:

whos:
  pattern: /whos
  controller: GooderCode
  action: whos

New view views/goodcodes.html:

{% extends "base.html" %}

{% block content %}
  {% for name in names %}
    {{ render('who', { name: name }) }}
  {% endfor %}
{% endblock %}

From here you can see that the controller defined at start can be also called from another view using render() in which case it will be rendered in that position in html. This has many uses, making views cleaner by dividing them in smaller pieces (also see: https://twig.symfony.com/doc/2.x/tags/include.html), collecting many views into one and so on.