Category Archives: silex
Working with jQuery and Silex as RestFull Resource provider
The previous post was about how to use AngularJS resources with Silex. AngularJS is great and when I need to switch back to jQuery it looks like I go back 10 years in web development, but business is business and I need to live with jQuery too. Because of that this post is about how to use the Silex RestFull resources from the previous post, now with jQuery. Let’s start:
We’re going to write a simple javascript object to handle the RestFull resource using jQuery:
var Resource = (function (jQuery) {
function Resource(url) {
this.url = url;
}
Resource.prototype.query = function (parameters) {
return jQuery.getJSON(this.url, parameters || {});
};
Resource.prototype.get = function (id, parameters) {
return jQuery.getJSON(this.url + '/' + id, parameters || {});
};
Resource.prototype.remove = function (id, parameters) {
return jQuery.ajax({
url:this.url + '/' + id,
xhrFields:JSON.stringify(parameters || {}),
type:'DELETE',
dataType:'json'
});
};
Resource.prototype.update = function (id, parameters) {
return jQuery.post(this.url + '/' + id, JSON.stringify(parameters || {}), 'json');
};
Resource.prototype.create = function (parameters) {
return jQuery.post(this.url, JSON.stringify(parameters || {}), 'json');
};
return Resource;
})(jQuery);
As you can see the library returns jQuery ajax promises, so we can use done() and error() callbacks to work with the server’s data.
Here our application example:
var host = document.location.protocol + '//' + document.location.host;
var resource = new Resource(host + '/api/message/resource');
resource.create({ id: 10, author: 'myname', message: 'hola'}).done(function (data) {
console.log("create element", data);
});
resource.query().done(function (data) {
console.log("query all", data);
});
resource.update(10, {message: 'hi'}).done(function (data) {
console.log("update element 1", data);
});
resource.get(10).done(function (data) {
console.log("get element 1", data);
});
resource.remove(10).done(function (data) {
console.log("remove element 1", data);
});
resource.query().done(function (data) {
console.log("query all", data);
});
And that’s all. You can get the full code of the example from my github account
Working with AngularJS and Silex as Resource provider
This days I’m playing with AngularJS. Angular is a great framework when we’re building complex front-end applications with JavaScript. And the best part is that it’s very simple to understand (and I like simple things indeed). Today we are going to play with Resources. Resources are great when we need to use RestFull resources from the server. In this example we’re going to use Silex in the backend. Let’s start.
First of all we must realize that resources aren’t included in the main AngularJS js file and we need to include angular-resource.js (it comes with Angular package). We don’t really need resources. We can create our http services with AngularJS without using this extra js file but it provides a very clean abstraction (at least for me) and I like it.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.min.js"></script>
We’re going to create a simple application with CRUD operations in the table. In the example we will use one simple SqlLite database (included in the github repository)
CREATE TABLE main.messages ( id INTEGER PRIMARY KEY NOT NULL , author VARCHAR NOT NULL , message VARCHAR NOT NULL );
Our main (and only one) html file:
<!DOCTYPE html>
<html ng-app="MessageService">
<head>
<title>Angular Resource Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.min.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="MessageController">
<h2>Message list <a href="#" ng-click="refresh()">refresh</a></h2>
<ul>
<li ng-repeat="message in messages">
<a href="#" ng-click="deleteMessage($index, message.id)">delete</a>
#{{message.id}} - {{message.author}} - {{message.message}}
<a href="#" ng-click="selectMessage($index)">edit</a>
</li>
</ul>
<form>
<input type="text" ng-model="author" placeholder="author">
<input type="text" ng-model="message" placeholder="message">
<button ng-click="add()" ng-show='addMode'>Create</button>
<button ng-click="update()" ng-hide='addMode'>Update</button>
<button ng-click="cancel()" ng-hide='addMode'>Cancel</button>
</form>
</body>
</html>
As we can see we will use ng-app=”MessageService” defined within the js/services.js file:
angular.module('MessageService', ['ngResource']).factory('Message', ['$resource', function ($resource) {
return $resource('/api/message/resource/:id');
}]);
And our controller in js/controllers.js:
function MessageController($scope, Message) {
var currentResource;
var resetForm = function () {
$scope.addMode = true;
$scope.author = undefined;
$scope.message = undefined;
$scope.selectedIndex = undefined;
}
$scope.messages = Message.query();
$scope.addMode = true;
$scope.add = function () {
var key = {};
var value = {author: $scope.author, message: $scope.message}
Message.save(key, value, function (data) {
$scope.messages.push(data);
resetForm();
});
};
$scope.update = function () {
var key = {id: currentResource.id};
var value = {author: $scope.author, message: $scope.message}
Message.save(key, value, function (data) {
currentResource.author = data.author;
currentResource.message = data.message;
resetForm();
});
}
$scope.refresh = function () {
$scope.messages = Message.query();
resetForm();
};
$scope.deleteMessage = function (index, id) {
Message.delete({id: id}, function () {
$scope.messages.splice(index, 1);
resetForm();
});
};
$scope.selectMessage = function (index) {
currentResource = $scope.messages[index];
$scope.addMode = false;
$scope.author = currentResource.author;
$scope.message = currentResource.message;
}
$scope.cancel = function () {
resetForm();
}
}
Now the backend part. As we said before we will use Silex. We’re going to use also RouteCollections to define our routes (you can read about it here). So our Silex application will be:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;
$app = new Silex\Application();
$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
$loader = new YamlFileLoader(new FileLocator(__DIR__ . '/config'));
$collection = $loader->load('routes.yml');
$routes->addCollection($collection);
return $routes;
}
);
$app->register(
new Silex\Provider\DoctrineServiceProvider(),
array(
'db.options' => array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db/app.db.sqlite',
),
)
);
$app->run();
We define our routes in the messageResource.yml
getAll:
path: /resource
defaults: { _controller: 'Message\MessageController::getAllAction' }
methods: [GET]
getOne:
path: /resource/{id}
defaults: { _controller: 'Message\MessageController::getOneAction' }
methods: [GET]
deleteOne:
path: /resource/{id}
defaults: { _controller: 'Message\MessageController::deleteOneAction' }
methods: [DELETE]
addOne:
path: /resource
defaults: { _controller: 'Message\MessageController::addOneAction' }
methods: [POST]
editOne:
path: /resource/{id}
defaults: { _controller: 'Message\MessageController::editOneAction' }
methods: [POST]
And finally our Resource controller:
<?php
namespace Message;
use Silex\Application;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class MessageController
{
public function getAllAction(Application $app)
{
return new JsonResponse($app['db']->fetchAll("SELECT * FROM messages"));
}
public function getOneAction($id, Application $app)
{
return new JsonResponse($app['db']
->fetchAssoc("SELECT * FROM messages WHERE id=:ID", ['ID' => $id]));
}
public function deleteOneAction($id, Application $app)
{
return $app['db']->delete('messages', ['ID' => $id]);
}
public function addOneAction(Application $app, Request $request)
{
$payload = json_decode($request->getContent());;
$newResource = [
'id' => (integer)$app['db']
->fetchColumn("SELECT max(id) FROM messages") + 1,
'author' => $payload->author,
'message' => $payload->message,
];
$app['db']->insert('messages', $newResource);
return new JsonResponse($newResource);
}
public function editOneAction($id, Application $app, Request $request)
{
$payload = json_decode($request->getContent());;
$resource = [
'author' => $payload->author,
'message' => $payload->message,
];
$app['db']->update('messages', $resource, ['id' => $id]);
return new JsonResponse($resource);
}
}
And that’s all. Our prototype is working with AngularJS and Silex as REST provider. We must take care about one thing. Silex and AngularJS aren’t agree in one thing about REST services. AngularJS removes the trailing slash in some cases. Silex (and Symfony) returns HTTP 302 moved temporaly when we’re trying to access to the resource without the trailing slash but when we’re working with mounted controllers we will obtain a 404 page not found (bug/feature?). That’s because my REST service is /api/message/resource/:id instead of /api/message/:id. If I chose the second one, when angular tries to create a new resource, it will POST /api/message instead of POST /api/message/. We’re using mounted routes in this example:
messages: prefix: /message resource: messageResource.yml
With one simple Silex application (without mounted routes) in one file it doesn’t happen (we will see HTTP 302 and a new request with the trailing slash). Because of that I use this small hack to bypass the problem.
You can see the full code of the example in my github account
Google App Engine, PHP and Silex. Setting up a Login Provider
Last week Google announced the PHP support for Google App Engine (GAE). PHPStorm, the great IDE for PHP development, also announced support for Google App Engine PHP. Because of that now is time to hack a little bit with this new toy.
I’ve worked in a couple of projects with Google App Engine in the past (with Python). With PHP the process is almost the same. First we need to define our application in the app.yaml file. In our example we are going to redirect all requests to main.php, where our Silex application is defined.
application: silexgae version: 1 runtime: php api_version: 1 threadsafe: true handlers: - url: .* script: main.php
To build a simple Silex application over Google App Engine is pretty straightforward (more info here). Because of that we’re going to go a little further. We are going to use the log-in framework provided by GAE to log-in with our Goggle account within our Silex application. In fact we can use the standard OAuth authentication process but Google provides a simple way to use our gmail account.
Now we’re going to build a LoginProvider to make this process simpler. Our base Silex application will be the following one:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Silex\Application;
use Gae\LoginProvider;
use Gae\Auth;
$app = new Application();
$app->register(new LoginProvider(), array(
'auth.onlogin.callback.url' => '/private',
'auth.onlogout.callback.url' => '/loggedOut',
));
/** @var Auth $auth */
$auth = $app['gae.auth']();
$app->get('/', function () use ($app, $auth) {
return $auth->isLogged() ?
$app->redirect("/private") :
"<a href='" . $auth->getLoginUrl() . "'>login</a>";
});
$app->get('/private', function () use ($app, $auth) {
return $auth->isLogged() ?
"Hello " . $auth->getUser()->getNickname() .
" <a href='" . $auth->getLogoutUrl() . "'>logout</a>" :
$auth->getRedirectToLogin();
});
$app->get('/loggedOut', function () use ($app) {
return "Thank you!";
});
$app->run();
Our LoginProvider is a simple Class that implements Silex\ServiceProviderInterface
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\UserService;
use Gae\Auth;
use Silex\Application;
use Silex\ServiceProviderInterface;
class LoginProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['gae.auth'] = $app->protect(function () use ($app) {
return new Auth($app, UserService::getCurrentUser());
});
}
public function boot(Application $app)
{
}
}
As you can see our Provider class proviedes us an instance of Gae\Auth class
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\User;
use google\appengine\api\users\UserService;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Silex\Application;
class Auth
{
private $user = null;
private $loginUrl;
private $logoutUrl;
private $logged;
public function __construct(Application $app, User $user=null)
{
$this->user = $user;
if (is_null($user)) {
$this->loginUrl = UserService::createLoginUrl($app['auth.onlogin.callback.url']);
$this->logged = false;
} else {
$this->logged = true;
$this->logoutUrl = UserService::createLogoutUrl($app['auth.onlogout.callback.url']);
}
}
/**
* @return RedirectResponse
*/
public function getRedirectToLogin()
{
return new RedirectResponse($this->getLoginUrl());
}
/**
* @return boolean
*/
public function isLogged()
{
return $this->logged;
}
/**
* @return string
*/
public function getLoginUrl()
{
return $this->loginUrl;
}
/**
* @return string
*/
public function getLogoutUrl()
{
return $this->logoutUrl;
}
/**
* @return \google\appengine\api\users\User|null
*/
public function getUser()
{
return $this->user;
}
}
And that’s all. Full code is available in my github account and you can also use composer to include this provider within your projects.
Sign-in with Twitter in a Silex application.
I’ve working in a pet-project with Silex and I wanted to perform a Sign-in with Twitter. Implementing Sign in with Twitter is pretty straightforward and it’s also well explained in the Twitter’s developers site. Now we only need to implement those HTTP client requests within PHP. We can create the REST client with curl but nowadays I prefer to use the great library called Guzzle to perform those kind of opperations. So let’s start.
The idea is to create something reusable. I don’t want to spend too much time including the Sign-in with Twitter in my proyects, so my first idea was to create a class with all the needed code and mount this class as group of Silex controllers (as it’s defined here). I also want to keep the class as standard as possible and avoiding the usage of any other external dependencies (except Guzzle)..
Imagine a simple Silex application:
<?php
// www/index.php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->get('/', function () {
return 'Hello';
});
$app->run();
Now I want to use a Sign-in with Twitter, so I will change the application to:
<?php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->register(new Silex\Provider\SessionServiceProvider());
$consumerKey = "***";
$consumerSecret = "***";
$twitterLoggin = new SilexTwitterLogin($app, 'twitter');
$twitterLoggin->setConsumerKey($consumerKey);
$twitterLoggin->setConsumerSecret($consumerSecret);
$twitterLoggin->registerOnLoggin(function () use ($app, $twitterLoggin) {
$app['session']->set($twitterLoggin->getSessionId(), [
'user_id' => $twitterLoggin->getUserId(),
'screen_name' => $twitterLoggin->getScreenName(),
'oauth_token' => $twitterLoggin->getOauthToken(),
'oauth_token_secret' => $twitterLoggin->getOauthTokenSecret()
]);
});
$twitterLoggin->mountOn('/login', function () {
return '<a href="/login/requestToken">login</a>';
});
$app->get('/', function () use ($app){
return 'Hello ' . $app['session']->get('twitter')['screen_name'];
});
$app->run();
The application will redirects all requests (without the correct session) to the route “/login”. The login page has a simple link to the route: “/login/requestToken” (we can create a fancy template with Twig if we want, indeed). This route redirects the request to Twitter’s login page and after a successful login it will redirects back to the route that we have defined within our Twitter application. The library assumes that this callback’s url is “/login/callbackUrl”. All this default routes can be defined by the user using the proper setters of the class.
When the sign-in is finished the application will trigger the callback defined in registerOnLoggin function and will redirects to the route “/”. This route (called internally “redirectOnSuccess”) is also customizable with a setter.
And that’s all. Library available at github and packagist
{
"require": {
"gonzalo123/silex-twitter-login": "dev-master"
}
}
Scaling Silex applications (part II). Using RouteCollection
In the post Scaling Silex applications I wanted to organize a one Silex application. In one comment Igor Wiedler recommended us to use RouteCollections instead of define the routes with a Symfony’s Dependency Injection Container. Because of that I started to hack a little bit about it and here I show you my outcomes:
I want to build an imaginary application with silex. This application has also one Api and one little blog. I want to organize those parts. Our index.php file
<?php
// www/index.php
require_once __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;
$app = new Application();
$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
$loader = new YamlFileLoader(new FileLocator(__DIR__ . '/../config'));
$collection = $loader->load('routes.yml');
$routes->addCollection($collection);
return $routes;
});
$app->run();
Now our routes.yml file:
# config/routes.yml
home:
path: /
defaults: { _controller: 'Gonzalo123\AppController::homeAction' }
hello:
path: /hello/{name}
defaults: { _controller: 'Gonzalo123\AppController::helloAction' }
api:
prefix: /api
resource: api.yml
blog:
prefix: /blog
resource: blog.yml
As we can see we have separated the main routing file into different files: api.yml (for the Api) and blog.yml (for the blog)
# config/api.yml
api.list:
path: /list
defaults: { _controller: 'Gonzalo123\ApiController::listAction' }
# blog.yml
blog.home:
path: /
defaults: { _controller: 'Gonzalo123\BlogController::homeAction' }
And now we can create our controllers:
<?php
// lib/Gonzalo123/AppController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
use Silex\Application;
class AppController
{
public function homeAction()
{
return new Response("AppController::homeAction");
}
public function helloAction(Application $app, $name)
{
return new Response("Hello" . $app->escape($name));
}
}
<?php
// lib/Gonzalo123/ApiController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class ApiController
{
public function listAction()
{
return new Response("AppController::listAction");
}
}
<?php
// lib/Gonzalo123/BlogController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class BlogController
{
public function homeAction()
{
return new Response("BlogController::homeAction");
}
}
And that’s all. Here also the needed dependencies within our composer.json file
{
"require":{
"silex/silex":"1.0.*@dev",
"symfony/yaml":"v2.2.0",
"symfony/config":"v2.2.0"
},
"autoload":{
"psr-0":{
"":"lib/"
}
}
}
source code at github.























