Introduction

Ammonio started as a personal project in 2011, now has a structured layout, a maintenance developing cycle and powers many production websites. You're free to take any parts you desire for studying, hacking, enjoying and of course for your business. Browse the sources on GitHub or get it via Composer.

Why we started such a project ? Because many web frameworks out there in the internet work, but sometimes they don't do exactly what you need, sometimes are buggy and you eventually ends up figuring out the bug, tracking it and depending on the product lifecycle for the fix, and sometimes you just feel better being in control of things, hacking the code exactly for your needs.

Some people think it's crazy running another framework on your own and maintaining it, but it actually turned out being a really growing and professional experience.

Lastly Ammonio doesn't have dependencies on any popular php components. Of course this was made on purpose.

Get Started

Install Ammonio framework (you need git) and check that everything works:

$ \curl -sSL http://ammonio.org/get | bash
$ am version

Quick start a new project:

$ am create newproject
new ammonio app created!

$ mysql -uroot -e "create database newproject"
$ cd newproject/
$ am syncdb
wrote '/Users/lc/devel/newproject/db/migrations/0to1.php'

$ am migrate 1
1
ok!

$ am adduser admin pass ADMIN
user admin created

$ am start
Running development webserver on http://localhost:8089

Browse to http://localhost:8089

Deploy an Nginx + PHP-FPM production environment

Here is a nginx + php-fpm configuration file:

server {

        listen       80;
        server_name  your.domain.com;
        root         /var/www/your.domain.com;


        location / {
                rewrite ^/bundle/(.+) /index.php?bundle&resource=$1 last;
                rewrite ^/favicon.ico /app/resources/images/favicon.ico last;
                try_files $uri /index.php?$query_string;
        }

        location ~ /resources/(.+) {
                try_files /app/resources/$1 /index.php?static&resource=$1;
        }

        location ~ \.php$ {
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_param APPLICATION_ENVIRONMENT "production";
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include /etc/nginx/fastcgi_params;
        }

}

Deploy an Apache production environment

And this is a Apache VirtualHost file:

<VirtualHost *:80>
        ServerAdmin info@domain.com
        ServerName your.domain.com

        SetEnv APPLICATION_ENVIRONMENT production

        DocumentRoot /var/www/your.domain.com/

        <Directory /var/www/your.domain.com>
                Options FollowSymLinks
                AllowOverride All
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/project_error.log

        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/project_access.log combined
</VirtualHost>

And this is the .htaccess file:

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/app/%{REQUEST_URI} -f
RewriteRule ^resources/(.+) /app/resources/$1 [L]
RewriteRule ^resources/(.+) /index.php?static&resource=$1 [L]
RewriteRule ^bundle/(.+) /index.php?bundle&resource=$1 [L]
RewriteRule ^favicon.ico /app/resources/images/favicon.ico [L]
RewriteCond %{SCRIPT_FILENAME} !resources
RewriteRule .* index.php [L]

Configuration

Configuration file is config.yml placed in root folder. You got general parameter and some of them are under environment structure:

environments:
    development:
        database:
            host: localhost
            user: root
            password:
            dbname: dbname
        debug: false
        debug_level: 3
        upload_repository: 'tmp'
        log_path: 'log/devel.log'
        authentication: 'Ammonio\Authentication\PlainAuthenticator'
        session_timeout: 5000
        cache_enable: 0
        twig_cache_enable: 0
        twig_cache_path: '/tmp'
        email_notifiche: 'luca.cervasio@gmail.com'
        mail:
            from: 'bot@moonar.it'
            fromname: 'Bot Moonar'
            smtp: 'smtp.gmail.com'
            username: 'bot@moonar.it'
            password: 'password'

Parameter can be accessed this way:

$k = Kernel::get_instance();
$parameter = $k->config['database']['host']

and the host parameter of the database subtree is returned, taking care of picking the right environment.

Routing

Routing is defined in config.yml. Every line is a route itself:

urls:
 /: [home, redirect]
 /login: [auth, index]
 /login/check: [auth, login]
 /logout: [auth, logout]
 /flush: [home, flush]
 /robots.txt: [home, robots]
 /sitemap.xml: [home, sitemap]
 /$lang: [home, index]
 /$lang/$controller/$action/$id: [$controller, $action]

The syntax is the YML hash syntax, key being the url and value an array of controller and action name.

In the example below the route /$lang: [home, index] will match both /it and /en and will call action index in controller home (file home_controller.php) passing language in the variable $params['lang'].

Controllers are resolved with the convention $controller_controller.php.

ORM & Models

First, read Orm Hate !

Define a book model in app/models/book.php:

<?php

class book extends \Ammonio\ORM\Entity {

    public $attributes = array(
        'title' => 'string',
        'author' => 'author',
        'year' => 'int',
    );

    public $hasMany = array (
    );

    public $constraints = array (

    );

}

And this is the author model in app/models/author.php:

<?php

    class author extends \Ammonio\ORM\Entity {

        public $attributes = array(
            'name' => 'string',
            'year_of_birth' => 'int'
        );

        public $hasMany = array (
            'books' => 'book'
        );

        public $constraints = array (

        );

    }

Attributes

The model can contains the $attributes, $has_many and $constraints attributes and as many methods you need to implement business logic.

Attribute types can be:

  • string
  • longtext
  • datetime
  • int
  • boolean
  • (any other model enabling a 1:n relation and browsing)

Constraints can be:

  • nullable
  • unique
  • stringpattern
  • maxlength

Specifying a 1:n relation

Once the relation is specified as in the example below, browsing it is fairly simple:

function my_action($params) {
    $book = book::get(2);         // get book with ID = 2
    render $book->author->name;   // browsing the 1:n relation
}

On the opposite you can get an author and browse to all its books:

function my_action($params) {
    $author = author::get($params['id']);
    foreach ($author->books as $book) {
        render $book->title;
    }
}

Scaffolding

Views

i18n

Services

Services are the place where we suggest you to put your business logic in.

Services che be scoped as well, the following scopes are available: - browser - user - account (when in multi-tenant you can have one service object per database account)

Filters

Cache

Authentication

Errors & Logging

Plugins

Plugin system lets users extend base functionalities. Plugins are installed in plugins folder.

Each plugin has the structure of a whole application and is actualy a mini-application itself: controllers, views, models and so on. Each plugin can hence define routes, have models and define controllers methods. A plugin can be easily moved from application to application in order to reuse code.

CLI

About

Ammonio is created by Luca Cervasio and is an opensource project sponsored by Moonar Engineering since 2011.

It is largely inspired by the Grails web framework. Comes with few dependencies (only Twig for the view tier) and it is (on purpose) not based on other popular components.