The way to install Slim recommended by its developers is through PHP Composer.
If you use a distribution like Arch Linux or based on this, Composer is in the official repositories, so you can install it with Pacman.
# pacman -S composer
In case not, to install composer writes in console the following command:
$ curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
Or if you prefer, you can use the following script 2 :
#! /bin/sh
EXPECTED_SIGNATURE= $( wget https://composer.github.io/installer.sig -O - -q )
php -r " copy('https://getcomposer.org/installer', 'composer-setup.php'); "
ACTUAL_SIGNATURE= $( php -r " echo hash_file('SHA384', 'composer-setup.php'); " )
if [ " $EXPECTED_SIGNATURE " = " $ACTUAL_SIGNATURE " ]
then
php composer-setup.php --quiet
RESULT= $?
rm composer-setup.php
exit $RESULT
else
>&2 echo ' ERROR: Invalid installer signature '
rm composer-setup.php
exit 1
fi
Saved as install-composer.sh to execute it in terminal with the command
$ sh install-composer.sh
Note : Through this method, we can keep composer updated, but you must move the composer.Phar file to the folder /usr/local/bin/ with the command
# mv composer.phar /usr/local/bin/composer
In this way you can execute composer writing only composer in console instead of php <Directorio>composer.phar .
If you are Windows user you must download the Composer-Setup.*.exe of the official composer repository in Github, who is at https://github.com/composer/windows-setup/releases/ and follow the instructions you give you the installer.
We can create a project from scratch or use the skeleton provided by Slim, which gives us a simple configuration to start the application, you just have to write the following in console:
$ composer create-proyect slim/slim-skeleton crud-slim
This will create a new crud-slim directory with the necessary files to start writing the application.
Directory structure
crud-slim
├── composer.json
├── composer.lock
├── CONTRIBUTING.md
├── dirstruct.txt
├── logs
│ ├── app.log
│ └── README.md
├── phpunit.xml
├── public
│ └── index.php
├── README.md
├── src
│ ├── dependencies.php
│ ├── middleware.php
│ ├── routes.php
│ └── settings.php
├── templates
│ └── index.phtml
├── tests
│ └── Functional
│ ├── BaseTestCase.php
│ └── HomepageTest.php
└── vendor/...
NOTE: The Board vendor/ contains many deputy director but it is not recommended to edit any of the files contained here since it is where all the units that we will use within the application and modify them would affect the operation of it are.
If we execute php -S localhost:8080 -t public public/index.php in the directory of our application and open our browser in the localhost:8080 the following view will appear
We create a database with the name slim
$ mysql -u[nombre-de-usuario] -p
> CREATE DATABASE slim COLLATE = 'utf8_unicode_ci';
> u slim
We add the usuarios table.
> CREATE TABLE usuarios ( ` id ` BIGINT NOT NULL AUTO_INCREMENT,
` nombre ` VARCHAR ( 250 ) NOT NULL ,
` correo ` VARCHAR ( 250 ) NOT NULL ,
` clave_acceso ` VARCHAR ( 250 ) NOT NULL ,
PRIMARY KEY ( ` id ` ));We create the database that we will use for the crud:
We create the user table:
Now that we have the database, we must add it to the Slim configuration. For this, we open the settings.php file located in the src directory and which contains the following:
<?php
return [
' settings ' => [
' displayErrorDetails ' => true , // set to false in production
' addContentLengthHeader ' => false , // Allow the web server to send the content-length header
// Renderer settings
' renderer ' => [
' template_path ' => __DIR__ . ' /../templates/ ' ,
],
// Monolog settings
' logger ' => [
' name ' => ' slim-app ' ,
' path ' => __DIR__ . ' /../logs/app.log ' ,
' level ' => Monolog Logger:: DEBUG ,
],
],
];
We add after the logger field the configuration of our database
//Configuración de base de datos para Slim
' db ' => [
' driver ' => ' mysql ' ,
' host ' => ' localhost ' ,
' database ' => ' slim '
'username' => '<tu nombre de usuario en mysql> ' ,
'password' => ' <tu contraseña> ' ,
'charset' => ' utf8',
' collation ' => ' utf8_unicode_ci ' ,
' prefix ' => '' ,
],
In addition, to automatically load the classes that we will create later, we must add in the composer.json file. JON A AUTOLOAD mapping with the PHP PSR-4 Convention.
"autoload" : {
"psr-4" : {
"App\" : "src/"
}
} Now you have to create the application model . Although Slim does not follow the MVC design pattern (model-vista-controller) in a conventional way, we should have an exclusive directory for each component, so we will create a directory for the model within src/ with our file explorer or with the mkdir modelos command from the src directory.
As we know, Slim does not have a tool for the default object-reequate mapping. However, it allows us to add one of another framework written in PHP; In this case we will use Eloquent 3 of Laravel.
To add eloquent to our crud we must first ask Composer to add it to the dependencies of our application.
$ composer require illuminate/database " ~5.1 " Then we add eloquent to the dependencies injection container (hereinafter CID ) of the application. We open the dependencies.php file that is in the src directory and we add it
$ container [ ' db ' ] = function ( $ container ) {
$ capsule = new Illuminate Database Capsule Manager ;
$ capsule -> addConnection ( $ container [ ' settings ' ][ ' db ' ]);
return $ capsule ;
}; To initialize eloquent in the application, it must also be added to the public/index.php before app->run();
$ capsule = $ app -> getContainer ()-> get ( ' capsule ' ); // toma el elemento capsule dentro del contenedor de la app
$ capsule -> bootEloquent (); // inicializa EloquentWe create the modelless class within the Board of Directors.
<?php
namespace App Modelos ;
//importa Eloquent para usarlo en el modelo
use Illuminate Database Eloquent Model as Eloquent ;
n
class ModeloUsuario extends Eloquent
{
// Define la llave primaria de la tabla usuarios
protected $ primaryKey = ' id ' ;
// Define el nombre de la tabla
protected $ table = ' usuarios ' ;
// Define los campos que pueden llenarse en la tabla
protected $ fillable = [
' nombre ' ,
' correo ' ,
' clave_acceso ' ,
];
} We will also create a controladores within src for application drivers. Once the directory is created, we will create within it a useful controller class that will handle the operation of the application. In addition, as our controllers will validate, we install the Validation 4 validation tool through composer .
$ composer require respect/validation
<?php
namespace App Controladores ;
use App Modelos ModeloUsuario as Usuario ; // para usar el modelo de usuario
use Slim Views Twig ; // Las vistas de la aplicación
use Slim Router ; // Las rutas de la aplicación
use Respect Validation Validator as v ; // para usar el validador de Respect
/**
* Clase de controlador para el usuario de la aplicación
*/
class ControladorUsuario
{
// objeto de la clase Twig
protected $ view ;
// objeto de la clase Router
protected $ router ;
/**
* Constructor de la clase Controller
* @param type SlimViewsTwig $view - Vista
* @param type SlimRouter $router - Ruta
*/
public function __construct ( Twig $ view , Router $ router )
{
$ this -> view = $ view ;
$ this -> router = $ router ;
}
/**
* Verifica que los parametros que recibe el controlador sean correctos
* @param type array $args - los argumentos a evaluar
*/
public function validaArgs ( $ args )
{
$ valid = [
// verifica que la id sea un entero
v:: intVal ()-> validate ( $ args [ ' id ' ]),
// verifica que se reciba una cadena de al menos longitud 2
v:: stringType ()-> length ( 2 )-> validate ( $ args [ ' nombre ' ]),
// verifica que se reciba un correo
v:: email ()-> validate ( $ args [ ' correo ' ]),
// verifica que no esté en blanco la contraseña
v:: notBlank ()-> validate ( $ args [ ' clave_acceso ' ])
];
}
/**
* Verifica la correctud de un conjunto de validaciones
* @param type array $validaciones - el conjunto de validaciones a evaluar
* @throws Exception cuando las validaciones no están en un arreglo
*/
public static function verifica ( $ validaciones )
{
if (! is_array ( $ validaciones ){
throw new Exception ( ' Las validaciones deben estar en un arreglo ' );
} else {
foreach ( $ validaciones as $ v ){
if ( $ v == false ) {
return false ; // todas las validaciones deben cumplirse para que sea correcto
}
}
return true ;
}
}
/*-- Funciones del CRUD --*/
}You also have to add the user to the application of the application so that it can use it.
$ container [ ' ControladorUsuario ' ] = function ( $ container ){
return new App Controladores ControladorUsuario ( $ container );
}; Recall that in Modelusuario we define a FILLABLE PART FOR THE TABLE, this is because id defined in the database as an increased auto attribute, then we only need to enter the other 3 fields to the database and we will do it with this function:
/**
* Función para crear un usuario
* @param type SlimHttpRequest $request - solicitud http
* @param type SlimHttpResponse $response - respuesta http
*/
public function crea ( $ request , $ response , $ args )
{
/*
getParsedBody() toma los parametros del cuerpo de $request que estén
como json o xml y lo parsea de un modo que PHP lo entienda
*/
$ param = $ request -> getParsedBody ();
$ validaciones = $ this -> validaArgs ( $ param ); // hace las validaciones
if ( verifica ( $ validaciones )){
// evalua si el correo ya existe en la base de datos
$ correo_existente = Usuario:: where ( ' correo ' , $ atr [ ' correo ' ])-> get ()-> first ();
// si el correo ya existe manda un error 403
if ( $ correo_existente ){
echo -> $ this -> error ( ' YA_ESTÁ_REGISTRADO_EL_CORREO ' ,
$ request -> getUri ()-> getPath (),
404 );
return $ this -> response -> withStatus ( 403 );
} else {
//crea un nuevo usuario a partir del modelo
$ usuario = new Usuario ;
// asigna cada elemento del arreglo $atr con su columna en la tabla usuarios
$ usuario -> nombre = $ atr [ ' nombre ' ];
$ usuario -> correo = $ atr [ ' correo ' ];
$ usuario -> clave_acceso = $ atr [ ' clave_acceso ' ];
$ usuario -> save (); //guarda el usuario
// crea una ruta para el usuario con su id
$ path = $ request -> getUri ()-> getPath () . ' / ' . $ usuario -> id ;
return $ response -> withStatus ( 201 ); // el usuario fue creado con éxito
}
}
} Here two functions are exemplified, one to show all registered users and another where a specific user shows. The structure of the templates lista.twig and usuario.twig mentioned will be explained in greater detail in the view section.
/**
* Obtiene todos los usuarios de la tabla usuarios y los manda a la vista
* @param type SlimHttpRequest $request - solicitud http
* @param type SlimHttpResponse $response - respuesta http
*/
public function listaUsuarios ( $ request , $ response , $ args )
{
/*
la vista manda un arreglo de usuarios con la respuesta http,
para que lo renderice en en el template lista.twig
*/
return $ this -> view -> render ( $ response , ' lista.twig ' , [ ' usuarios ' => Usuario:: all ()]);
}
/**
* Busca un usuario por su id
* @param type SlimHttpRequest $request - la solicitud http
* @param type SlimHttpResponse $response - la respuesta http
* @param type array $args - argumentos para la función
*/
public function buscaUsuarioID( $ request , $ response , $ args )
{
$ id = $ args [ ' id ' ];
$ valid = [v:: intVal ()-> validate ( $ id )]; // verifica que la id sea un entero
n
// si la validación es correcta
if ( $ valid == true ){
$ usuario = Usuario:: find ( $ id ); // busca la id en la tabla usuarios
if ( $ usuario ){
/*
si el usuario es encontrado, manda una respuesta con éste
y lo renderiza en el template usuario.twig
*/
return $ this -> view -> render ( $ response , ' usuario.twig ' , $ usuario );
} else {
/*
Si no hay un usuario con la id de los parametros, entonces obtiene la uri de la solicitud,
redirecciona a la lista de usuarios y regresa una respuesta con la uri y un status 404 (not found)
*/
$ status = 404 ;
$ uri = $ request -> getUri ()-> withQuery ( '' )-> withPath ( $ this -> router -> pathFor ( ' listaUsuarios ' ));
return $ response -> withRedirect (( string ) $ uri , $ status );
} else {
// si la validación es falsa, regresa un error de bad request
return $ response -> withStatus ( 400 );
}
} Example of a function to update a user.
/**
* Actualiza un usuario
* @param type SlimHttpRequest $request - la solicitud http
* @param type SlimHttpResponse $response - la respuesta http
* @param type array $args - argumentos para la función
*/
public function actualiza ( $ request , $ response , $ args )
{
// busca un usuario la id del arreglo de parametros en la tabla usuarios
$ usuario = Usuario:: find (( int ) $ args [ ' id ' ]);
if (! $ usuario ){
/*
Si no hay un usuario con la id de los parametros, entonces obtiene la uri de la solicitud,
redirecciona a la lista de usuarios y regresa una respuesta con la uri y un estado 404 (not found)
*/
$ status = 404 ;
$ uri = $ request -> getUri ()-> withQuery ( '' )-> withPath ( $ this -> router -> pathFor ( ' listaUsuarios ' ));
return $ response -> withRedirect (( string ) $ uri , $ status );
} else {
$ data = $ request -> getParsedBody (); // guarda los argumentos de la solicitud en un arreglo
$ validaciones = $ this -> valida ( $ data ); // valida los datos
if ( verifica ( $ validaciones )){
$ usuario -> update ( $ data ); // Eloquent actualiza la información en la tabla
// regresa una respuesta con la uri y redirecciona a la vista especifica del usuario
$ uri = $ request -> getUri ()-> withQuery ( '' )-> withPath ( $ this -> router -> pathFor ( ' usuario ' , [ ' id ' => $ usuario -> id ]));
return $ response -> withRedirect (( string ) $ uri );
}
}
} Eloquent has three ways to eliminate elements from a table: the first is to eliminate an instance of the model from which its primary key is not known, it uses the delete function but its disadvantage is that you have to recover the entire instance before calling delete ; The second is, assuming that the primary key of the model is known, calls the destroy function that eliminates the model without having to recover the complete instance; The third option is by consultations, for example $eliminados = Usuario::where('nombre','like','C%')->delete(); I would eliminate all users whose name begins with "C". Additionally, Eloquent has soft deeting , that is, the model is not deleted from the database, but an deleted_at attribute is added and, as recommended by Laravel developers, a column should be added to the table to contain said attributes. To enable the Soft Deosting method in the application you must add the IlluminateDatabaseEloquentSoftDeletes in the app models. The following example uses delete to make validations and give more robustness before eliminating the models, you are free to use any of the available options.
/**
* Elimina un usuario
* @param type SlimHttpRequest $request - la solicitud http
* @param type SlimHttpResponse $response - la respuesta http
* @param type array $args - argumentos para la función
*/
public function elimina ( $ request , $ response , $ args )
{
$ usuario = Usuario-> find ( $ args [ ' id ' ]);
$ validaID = [v:: intVal ()-> validate ( $ id )];
if ( $ usuario && $ validaID ){
// si existe el usuario y la validación es correcta, lo elimina
$ usuario -> delete ();
}
/*
regresa una respuesta con la uri y redirecciona a la lista de usuarios,
se haya o no eliminado el usuario
*/
$ uri = $ request -> getUri ()-> withQuery ( '' )-> withPath ( $ this -> router -> pathFor ( ' listaUsuarios ' ));
return $ response -> withRedirect (( string ) $ uri );
}
The implementation of Slim routes was built from Fastroute 5 and provides methods to be able to work with the most commonly used HTTP methods, that is, GET , Post , Put , Delete , Patch , Options that can be handled one by one or all in a way to generate with the any() Slim method. In addition, it is possible to handle several methods on a single route using the map() function. In our application, the routes are found in the src/routes.php file that contains the route that loads the view of the initial skeleton page, we do not need it then you can remove or comment to guide you when creating the other routes. We will use the get methods to load views and see users, post to create users, Patch to update a user and delegate to delete.
The routes that only handle HTTP GET requests use the get() method of Slim, which receives as arguments a route pattern (with optional position markers) and a callback function that can come from a controller or declare on the same route.
$ app -> get ( ' / ' , function ( $ request , $ response , $ args ){
return $ this -> view -> render ( $ response , " index.twig " );
})-> setName ( ' inicio ' ); What this route is doing is, for the "/" pattern (which would be the initial pattern of the server) to call a function that returns in response the view defined in the Template index.twig already this route assigns the name "Home" so that the views can interpret them more easily.
// ruta para cargar la vista de todos los usuarios registrados
$ app -> get ( ' /listaUsuarios ' , function ( $ request , $ response , $ args ){
return $ this -> view -> render ( $ response , ' listaUsuarios.twig ' );
})-> setName ( ' listaUsuarios ' );
/*
ruta para cargar la vista de un usuario en especifico definido por su id
empleando la función buscaUsuarioID() de la clase ControladorUsuario,
previamente agregada al CID de la aplicación
*/
$ app -> get ( ' /listaUsuarios/{id} ' , ' ControladorUsuario:buscaUsuarioID ' )-> setName ( ' usuario.ver ' );
// ruta para cargar el formulario para crear usuario
$ app -> get ( ' /nuevo ' , function ( $ request , $ response , $ args ){
return $ this -> view -> render ( $ response , ' formulario_crea.twig ' );
})-> setName ( ' usuario.crear ' );
// ruta para cargar el formulario para actualizar usuario
$ app -> get ( ' /listaUsuarios/{id}/actualiza ' , function ( $ request , $ response , $ args ){
return $ this -> view -> render ( $ response , ' formulario_actualiza.twig ' );
})-> setName ( ' usuario.editar ' );
As with GET applications, SLIM has a post() function to handle post requests. This function also receives as parameters the pattern of the route (with optional position markers) and a callback function.
// ruta para crear un nuevo usuario
$ app -> post ( " /nuevo " , " ControladorUsuario:crea " ); It can be noted that this route does not use the setName() function because there are already a route with the same pattern ("/new") but using different methods, both can share the same name.
For Patch, what is mentioned above is also fulfilled for get and post . So, to update, we would have some of this style:
// ruta para actualizar un usuario
$ app -> patch ( ' listaUsuarios/{id} ' , ' ControladorUsuario:actualiza ' )-> setName ( ' usuario.actualizar ' ); // ruta para eliminar un usuario
$ app -> delete ( ' listaUsuario/{id} ' , ' ControladorUsuario:elimina ' )-> setName ( ' usuario.eliminar ' );We already know that Slim does not have a tool to generate the templates of their views and, in fact, the views are only part of the body of the PSR-7 HTTP responses that Slim implements so they necessarily depend on the routes. However, they can be provided by component component components and they themselves provide the implementations of two of these components, TWIG and PHP-View . I personally prefer Twig since it caused me less problems and in general it has a clearer structure because its syntax is based on Jinja and Django Templates. Of course, like everything in Slim, the use of components is a matter of taste and other tools can be handled to generate our views.
First, and as with the rest of the units, we will add TWIG using composer .
$ composer require slim/twig-view
Note : If you want to use PHP-View instead of Twig , it only replaces slim/twig-view with slim/php-view
After installing TWIG , it must also be added to the CID of the application so that Slim registers it as one of the services and can use it.
$ container [ ' view ' ] = function ( $ c ) {
$ settings = $ c -> get ( ' settings ' )[ ' renderer ' ]; //nos indica el directorio donde están las plantillas
$ view = new Slim Views Twig ( $ settings [ ' template_path ' ], [
' cache ' => false ,]); // puede ser false o el directorio donde se guardará la cache
// instancia y añade la extensión especifica de slim
$ basePath = rtrim ( str_ireplace ( ' index.php ' , '' , $ c [ ' request ' ]-> getUri ()-> getBasePath ()), ' / ' );
$ view -> addExtension ( new Slim Views TwigExtension ( $ c [ ' router ' ], $ basePath ));
return $ view ;
};In a basic Twig structure we will find 3 types of delimitors:
As the Twig design is based on templates, we can create a layout.twig base template and inherit the rest of the templates.
<! DOCTYPE html>
<html>
<head>
<title> CRUD SLIM </title>
<meta charset="utf- 8 ">
<meta name="viewport" content="width=device-width, initial-scale= 1 ">
{% block stylesheets %}
{ # Aquí incluimos los archivos CSS o CDN de CSS que usemos #}
<link href="url de cdn" type="text/css" rel="stylesheet" />
{ # la función base_url() le indica a twig que busque desde el directorio raíz de proyecto #}
<link href="{{ base_url() }}/directorio/de/css" type="text/css" rel="stylesheet" />
{% endblock %}
{%block scripts }
{ # Aqui incluimos los .js y otros scripts
<script src="{{ base_url() }}/directorio/de/scripts"></script>
{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</div>
</body>
</html> We can have the templates inherit from layout.twig in another directory, for example templates/crud to keep the hierarchy organized between them.
If we remember the routes, the view that loads when starting the application is index.twig that would have a structure like the following:
{% extends ' layout.twig ' %}
{% block content %}
{ # la función path_for('') llama la ruta con el nombre que recibe como parametro #}
<a href="{{ path_for ( ' listaUsuarios ' ) }} " >Lista los usuarios registrados</a>
<a href= " {{ path_for ( ' usuario.crear ' ) }} " >Agrega un nuevo usuario</a>
{% endblock %}
Form formulario_crea.twig
{% extends ' layout.twig ' %}
{% block content %}
{ # manda los datos del formulario a la ruta 'usuario.crear' con un método post #}
<form action="{{ path_for ( ' usuario.crear ' ) }} " method= " post" autocomplete="off">
<label for="nombre">Nombre
<input type="text" name="nombre" id="nombre" placeholder="Escribe tu nombre">
</label>
<label for="correo">Correo
<input type="email" name="correo" id="correo" placeholder="Escribe tu correo">
</label>
<label for="clave_acceso">Contraseña
<input type="password" name="clave_acceso" id="clave_acceso" placeholder="Escribe tu contraseña">
<button type="submit">Agregar</button>
{% endblock %} Form of formulario_actualiza.twig
{% extends ' layout.twig ' %}
{% block content %}
{ # manda los datos del formulario a la ruta 'usuario.actualizar' con un método patch #}
<form action="{{ path_for ( ' usuario.actualizar ' ) }} " method= " patch" autocomplete="off">
<label for="nombre">Nombre
<input type="text" name="nombre" id="nombre" placeholder="Escribe tu nombre">
</label>
<label for="correo">Correo
<input type="email" name="correo" id="correo" placeholder="Escribe tu correo">
</label>
<label for="clave_acceso">Contraseña
<input type="password" name="clave_acceso" id="clave_acceso" placeholder="Escribe tu contraseña">
<button type="submit">Actualiza</button>
{% endblock %} listaUsuario.twig structure
{% extends ' layout.twig ' %}
{% block content %}
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Correo</th>
</tr>
</thead>
<tbody>
{ # itera la tabla usuarios del modelo #}
{% for usuario in usuarios %}
<tr>
<td>{{ usuario.nombre }}</td>
<td>{{ usuario.correo }}</td>
<td><a href="{{ path_for ( ' usuario.ver ' ) }} " >ver</a></td>
<td><a href= " {{ path_for ( ' usuario.eliminar ' ) }} " >eliminar</a><td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %} usuario.twig structure.twig
{% extends ' layout.twig ' %}
{% block content %}
<h1>{{ usuario.nombre }}</h1>
<h2>{{ usuario.correo }}</h2>
<img src="http: //i.imgur.com/Y9IVuHg.jpg"/>
{% endblock %}Remember that the premise of this framework is to use only what you consider necessary and no more than that; The scale of your project is defined by yourself and not by the framework.
1: Mariadbcom. (2016). Mariadbcom. Retrieved 25 SEPTEMBER, 2016, FROM https://mariadb.com/blog/why-should-you-migrate-mysql-mariadb.
2: How to Install Composer Programmmatically?#. (ND). Retrieved SEPTEMBER 25, 2016, FROM https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md.
3: Eloquent: Getting Started - Laravel - The PHP Framework for Web Artisans. (ND). Retrieved SEPTEMBER 29, 2016, from https://lavel.com/docs/5.1/eloquent
4: Effective Validation with respect. Retrieved SEPTAMBER 30, 2016, FROM https://websec.io/2013/04/01/Effective-validation-with-respect.html
5: N. (2016). Fasttroute. Retrieved 12 October, 2016, from https://github.com/nikic/fastroute
Codecourse (2016, April 13). Authentication with Slim 3 Retrieved from https://www.youtube.com/playlist?ist=Plfdthywgc_yy90xrdq6mrww042aec
Rob Allen's Devnotes. (2016, July 28). Retrieved November 08, 2016, from https://akrabat.com/category/slim-framework/