WebMardi: lithium
Lithium
the most RAD framework for PHP 5.3+
Agenda
- The basics
- Advanced stuff
- How and why do we use it at QoQa.ch
Disclaimer
- I don't know Symfony(2), CakePHP, Fuel, Yii, ZF(2) …
- Framework sceptic
- Did PHP, Perl, Rails, Django, …, JSP.
Lithium is
- a Cake3 fork (2y.o.)
- in development (0.10)
- http://lithify.me
- irc://irc.freenode.net/#li3
What I think it is…
- not for n00bs
- a great glue
- in development
Why?
- PHP 5.3
- PSR-0
- consistency
- everything is hackable
People ❤ numbers
|
Lithium |
Symfony 2 |
License |
BSD |
MIT |
Devs |
~80 |
~425 |
kLOC |
~90 |
~750 |
Dive
$ git clone https://github.com/UnionOfRAD/framework.git webmardi
$ cd webmardi
$ git submodule init && git submodule update
$ chmod -R 0777 app/resources
app/webroot/index.php
<?php
require dirname(__DIR__) . '/config/bootstrap.php';
// return a lithium\action\Response
echo lithium\action\Dispatcher::run(new lithium\action\Request());
Bootstrap
<?php
define('LITHIUM_APP_PATH', dirname(dirname(__DIR__)));
define(
'LITHIUM_LIBRARY_PATH',
dirname(LITHIUM_APP_PATH) . '/libraries'
);
include LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';
use lithium\core\Libraries;
Libraries::add('lithium');
Libraries::add('app', array('default' => true));
Dispatcher::run()
- Routes the request
- Instanciate a controller
- Call that controller
- Return the Response
app/config/routes.php
<?php
use lithium\net\http\Router;
// MongoDB specific id
Router::connect(
'/{:controller}/{:action}/{:id:[0-9a-f]{24}}.{:type}',
array('id' => null)
);
Router::connect('/{:controller}/{:action}/{:id:[0-9a-f]{24}}');
Router::connect('/{:controller}/{:action}/{:args}');
app/controllers/PagesController.php
<?php
namespace app\controllers;
class PagesController extends \lithium\action\Controller {
public function view() {
$message = 'Hello World!';
return compact('message');
}
}
app/view/pages/view.html.php
<?php
$this->title($message);
?>
<h1><?= $this->title() ?></h1>
<p>indeed!</p>
Short tags?
<?= $foo ?>
// means
<?php echo $h($foo) ?>
// $h() is
<?php echo htmlspecialchars((string) $data, ENT_QUOTES, $encoding) ?>
app/view/layouts/default.html.php
<!DOCTYPE html>
<html>
<meta charset=utf-8>
<title><?= $this->title() ?></title>
<?= $this->content() ?>
</html>
app/models/Users.php
<?php
namespace app\models
class Users extends \lithium\data\Model {
}
app/config/bootstrap/connections.php
<?php
use lithium\data\Connections;
Connections::add('default', array(
'host' => 'localhost',
'type' => 'MongoDb',
'database' => 'my_app',
)));
find
<?php
Users::find(1); // or mongo id
Users::findByEmail('yoan.blanc@gmail.com');
Users::find(array(
'conditions' => array(
'email' => 'yoan.blanc@gmail.com'
)
);
app/models/Users.php
<?php
namespace app\models
class Users extends \lithium\data\Model {
public $validates = array(
'email' => array(
array('notEmpty', 'message' => 'is required'),
array('email', 'message' => 'not a valid e-mail'),
),
);
public function fullname($entity) {
return $entity->firstname . ' ' . $entity->lastname;
}
}
Advanced config
<?php
use lithium\data\Connections;
Connections::add('default', array(
true => array(
'type' => 'MongoDb',
'host' => 'localhost'
),
'production' => array(
'database' => 'my_app'
),
'development' => array(
'database' => 'my_app_dev'
),
'test' => array(
'database' => 'my_app_test'
)
));
app/config/boostrap/libraries.php
PSR-0 FTW!
<?php
use lithium\core\Libraries;
Libraries::add('Imagine', array(
'prefix' => 'Imagine\\',
'path' => LITHIUM_LIBRARY_PATH . '/Imagine/lib/Imagine',
));
Libraries::add('zf2', array(
'prefix' => 'Zend\\',
'path' => LITHIUM_LIBRARY_PATH . '/zf2/library/Zend',
));
Plugins
- Doctrine, PHP-AR, Twig,
- SwiftMailer, PHPMailer,
- Flash messages,
- Markdown, less,
- PDF,
- Delayed Job, Gearman,
- OAuth, Facebook, …
Filters, Monkeypatching
or Aspect-Oriented Programming (AOP)
<?php
use lithium\action\Dispatcher;
Dispatcher::applyFilter('run', function($self, $params, $chain) {
xhprof_enable();
$data = $chain->next($self, $params, $chain);
$xhprof_data = xhprof_disable();
// save the $xhprof_data
return $data
});
QoQa2
- 2007-2011 †
- Symfony 1.0.7
- Doctrine
- qoqa.fr and qwine.ch built using
cp -r
QoQa3
- Lithium
- PHP ActiveRecord
- utf-8
- Memcache
- PHP-FPM, NGinx
Multishop architecture
Templates for everything
- HTML views
- XML (Atom/RSS)
- Emails
- PDF (via wkhtmltopdf)
Multilingual strings
<!DOCTYPE html>
<meta charset=utf-8>
<title><?= $t('Hello World!') ?></title>
<p><?=$t('We live in Switzerland after all!' ) ?></p>
<p><?=$tn(
'{:count} visitor',
'{:count} visitors',
$count
) ?></p>
</html>
Using .po files but not gettext directly
Multilingual views
<?php
use lithium\net\http\Media;
Media::type('html', 'text/html', array(
'view' => 'lithium\\template\\View',
'paths' => array(
'template' => array(
'{:library}/views/{:controller}/{:template}.{:locale}.{:type}.php',
'{:library}/views/{:controller}/{:template}.{:type}.php',
),
'layout' => array(
'{:library}/views/layouts/{:layout}.{:locale}.{:type}.php',
'{:library}/views/layouts/{:layout}.{:type}.php',
),
…
),
));
cont.
1. /qoqa.ch/views/spam/foo.fr.html.php
2. /qoqa.ch/views/spam/foo.html.php
3. /libraries/qshop/views/spam/foo.fr.html.php
4. /libraries/qshop/views/spam/foo.html.php
Filters
<?php
use lithium\g11n\Message;
Message::applyFilter('_translated', function($self, $params, $chain) {
$tr = $chain->next($self, $params, $chain);
if (is_null($tr)) {
trigger_error(
'Translation not found '.$params['locale'].': '.$params['id']
);
}
return $tr;
});
Cache
<?= $this->view()->render(
array('element' => 'offer/deal'),
compact('deal', 'offer', 'locale'),
array(
'cache' => '+5 minutes',
'key' => $deal->id,
)
) ?>
template, controller and custom levels.
And many more
ask your questions now!
Wanted
webdev » jobs@qoqa.com