Weasley is a PHP package that provides generators, helpers, and utility classes for the Slytherin. Its goal is to improve the overall productivity when writing web applications based on Slytherin by reducing in writing code related to CRUD operations.
Install Weasley through Composer:
$ composer require rougin/weasleyWeasley is built on a layered architecture where each domain delegates to a dedicated package:
| Package | Purpose |
|---|---|
| Slytherin | Simple, extensible PHP micro-framework. |
| Onion | HTTP middlewares for Slytherin. |
| Valla | A simple validation package in PHP. |
| Blueprint | A bootstrap for PHP console projects. |
| Classidy | Create PHP classes using PHP. |
Weasley provides commands that generate code for Slytherin components. These commands allow Slytherin to be a rapid prototyping tool when creating web-based applications.
Use the weasley command to access the list of its available commands:
$ vendor/bin/weasley| Command | Description |
|---|---|
make:check |
Creates a new validation (Check) class |
make:handler |
Creates a new HTTP Middleware class |
make:package |
Creates a new Slytherin Integration class |
make:route |
Creates a new HTTP route class |
Each command accepts the following options:
$ vendor/bin/weasley make:check UserCheck --path src/Checks --namespace App\Checks --author "John Doe"| Option | Default | Description |
|---|---|---|
--path |
src/Checks |
Directory where the file will be created |
--namespace |
App\Checks |
Namespace for the generated class |
--author |
(empty) | Author name in the class docblock |
Weasley provides classes for creating HTTP routes in the RESTful style. In other PHP frameworks, these are also known as Controllers.
A simple HTTP route class that provides a json() helper for returning JSON responses:
use Rougin\Weasley\Route;
class Welcome extends Route
{
public function index()
{
$data = array('message' => 'Hello world!');
return $this->json($data);
}
}The Route class is a root-namespace alias for Rougin\Weasley\Routes\HttpRoute. It accepts the PSR-07 request and response through its constructor:
/** @var \Psr\Http\Message\ServerRequestInterface */
$request = /** ... */;
/** @var \Psr\Http\Message\ResponseInterface */
$response = /** ... */;
$route = new Welcome($request, $response);
/** @var \Psr\Http\Message\ResponseInterface */
$result = $route->index();Extends HttpRoute with built-in CRUD operations backed by an Eloquent model and a validator:
use Rougin\Weasley\Routes\JsonRoute;
class UsersRoute extends JsonRoute
{
protected $model = 'Acme\Models\User';
protected $mutator = 'Rougin\Weasley\Mutators\RestMutator';
protected $validator = 'Acme\Checks\UserCheck';
}Once defined, the following methods become available:
| Method | HTTP Equivalent | Description |
|---|---|---|
index() |
GET /users |
Returns all records (paginated when illuminate/pagination is installed) |
show($id) |
GET /users/{id} |
Returns a single record |
store() |
POST /users |
Creates a new record from the parsed request body |
update($id) |
PUT /users/{id} |
Updates an existing record |
delete($id) |
DELETE /users/{id} |
Deletes a record |
The $model must be an Eloquent model with $fillable defined. The $validator must be a Check subclass. Both are validated at construction time and will throw UnexpectedValueException if missing.
Weasley provides HTTP middlewares (handlers) that process incoming requests and outgoing responses. Each handler implements Rougin\Slytherin\Middleware\MiddlewareInterface.
Note
Starting v0.8, all handler classes are thin wrappers over Onion (rougin/onion). Every feature described below originates from Onion's classes.
Adds CORS headers to every response:
use Rougin\Weasley\Handlers\AllowCrossOrigin;
// Default: allows all origins, allows GET/POST/PUT/DELETE/OPTIONS
$cors = new AllowCrossOrigin;
// Restrict to specific origins and methods via constructor
$cors = new AllowCrossOrigin(
array('https://example.com', 'https://api.example.com'),
array('GET', 'POST')
);
// Or configure fluently
$cors = (new AllowCrossOrigin)
->allowed(array('https://example.com'))
->methods(array('GET', 'POST', 'DELETE'));Note
If the incoming request method is OPTIONS, the handler returns an empty response immediately (preflight support).
Converts empty, "null", and "undefined" string values from query parameters and parsed body to actual null:
use Rougin\Weasley\Handlers\EmptyStringToNull;
$handler = new EmptyStringToNull;
// query params: ?age=&name=null&role=undefined
// becomes: ['age' => null, 'name' => null, 'role' => null]Sets the Content-Type header to application/json on every response that does not already have one:
use Rougin\Weasley\Handlers\JsonContentType;
$handler = new JsonContentType;
// Response header: Content-Type: application/jsonAn extensible base class for transforming request values. Extend it and override the transform() method:
use Rougin\Weasley\Handlers\MutateRequest;
class SanitizeHtml extends MutateRequest
{
protected function transform($value)
{
return is_string($value) ? strip_tags($value) : $value;
}
}The transform() method is called recursively on every value in query parameters and the parsed body. Arrays are recursed into automatically.
Replaces the HTTP verb of the request with the value of a configurable key from the parsed body, enabling HTML forms to simulate PATCH, PUT, or DELETE:
use Rougin\Weasley\Handlers\SpoofHttpMethod;
// Default key is "_method"
$spoof = new SpoofHttpMethod;
// Use a custom key
$spoof = new SpoofHttpMethod('_action');
// or fluently: $spoof->key('_action');When the request body contains ['_method' => 'PATCH'], the request method becomes PATCH.
Trims whitespace from all string values in query parameters and the parsed body:
use Rougin\Weasley\Handlers\TrimStringValue;
$handler = new TrimStringValue;
// query params: ?name= Rougin
// becomes: ['name' => 'Rougin']Weasley provides the Check class for validating data. It is built on Valla (rougin/valla) and supports all rules from Valla's rule engine (e.g., required, email, and more):
$check = new UserCheck;
$data = /* e.g., from request */;
if ($check->valid($data))
{
// Data passed validation
}
else
{
// Get all errors: array<string, string[]>
$errors = $check->errors();
// Get the first error only
echo $check->firstError(); // e.g., "Age is required"
}Error messages are built from the label and the rule description. For example, a missing email field with the label "Email" produces "Email is required".
The method-based style is recommended when rules depend on the submitted data (e.g., conditionally requiring a field):
use Rougin\Weasley\Check;
class UserCheck extends Check
{
/**
* @return array<string, string>
*/
public function labels()
{
return array(
'name' => 'Name',
'email' => 'Email',
'age' => 'Age',
);
}
/**
* @param array<string, mixed> $data
*
* @return array<string, string>
*/
public function rules(array $data)
{
$rules = array(
'name' => 'required',
'email' => 'required|email',
'age' => 'required|numeric|min:18',
);
return $rules;
}
}This can also define rules and labels as protected properties for simple, static rule sets:
use Rougin\Weasley\Check;
class SimpleCheck extends Check
{
protected $labels = array(
'name' => 'Name',
'email' => 'Email',
);
protected $rules = array(
'name' => 'required',
'email' => 'required|email',
);
}Mutators transform data into a specified output format (e.g., PSR-07 responses, API payloads). They implement Rougin\Weasley\Contract\Mutator.
Encodes data as JSON and returns a PSR-07 response:
use Rougin\Weasley\Mutators\JsonMutator;
$mutator = new JsonMutator($response);
$result = $mutator->mutate(array('status' => 'success'));
// Content-Type: application/json
// Body: {"status":"success"}A second argument accepts JSON encoding options:
$mutator = new JsonMutator($response, JSON_PRETTY_PRINT);Formats paginated results following PayPal's API Style Guide:
use Rougin\Weasley\Mutators\RestMutator;
$mutator = new RestMutator;
$result = $mutator->mutate($paginator);
// $result becomes:
// [
// 'total_items' => 100,
// 'total_pages' => 10,
// 'items' => [...],
// ]When the data is not a LengthAwarePaginator, the mutator wraps it as ['items' => $data].
Weasley provides IntegrationInterface implementations for wiring third-party packages into Slytherin's container.
Enables Eloquent ORM from Laravel:
$ composer require illuminate/databaseuse Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Weasley\Packages\Laravel\Eloquent;
$config = new Configuration;
$config->set('database.default', 'sqlite');
$config->set('database.sqlite.driver', 'sqlite');
$config->set('database.sqlite.database', ':memory:');
$container = new Container;
(new Eloquent)->define($container, $config);
// Eloquent models are now available globally
$users = User::all();Enables Blade templating:
$ composer require illuminate/viewuse Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Weasley\Packages\Laravel\Blade;
$config = new Configuration;
$config->set('illuminate.view.templates', __DIR__ . '/templates');
$config->set('illuminate.view.compiled', __DIR__ . '/cache');
$container = new Container;
(new Blade)->define($container, $config);
// Resolve the renderer from the container
$renderer = $container->get('Rougin\Weasley\Renderers\BladeRenderer');
echo $renderer->render('welcome', array('name' => 'Rougin'));Adds pagination support to Eloquent models:
$ composer require illuminate/paginationuse Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Weasley\Packages\Laravel\Paginate;
// Assuming Eloquent is already booted (see above)
$container = new Container;
(new Paginate)->define($container, new Configuration);
// Eloquent models now have a paginate() method
$paginator = User::paginate(10); // 10 per page
echo $paginator->total(); // total records
echo $paginator->currentPage(); // current page numberProvides session management through SessionHandlerInterface:
use Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Weasley\Packages\Session;
$config = new Configuration;
$config->set('session.cookies', array());
$config->set('session.expiration', 3600);
$config->set('session.path', __DIR__ . '/sessions');
$container = new Container;
(new Session)->define($container, $config);
// Resolve the session from the container ---
$class = 'Rougin\Weasley\Contract\Session';
$session = $container->get($class);
// ------------------------------------------
$session->set('user_id', 42);
echo $session->get('user_id'); // 42
$session->regenerate(); // rotate session ID
$session->delete('user_id'); // remove a keyWeasley defines interfaces for its extensible components:
namespace Rougin\Weasley\Contract;
interface Mutator
{
/**
* @param mixed $data
* @return mixed
*/
public function mutate($data);
}Implement this interface to create custom mutators. JsonMutator and RestMutator are built-in implementations.
namespace Rougin\Weasley\Contract;
interface Session
{
/**
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null);
/**
* @param string $key
* @param mixed $value
* @return self
*/
public function set($key, $value);
/**
* @param string $key
* @return boolean
*/
public function delete($key);
/**
* @param boolean $delete
* @return boolean
*/
public function regenerate($delete = false);
}Generates cryptographically secure random strings:
use Rougin\Weasley\Assorted\Random;
$token = Random::make(32); // e.g., "a7f3b9c2d1e8..."Please see CHANGELOG for more recent changes.
As Weasley continues to evolve, there might be some breaking changes in its internal code during development. The said changes can be found in UPGRADING.
See CONTRIBUTING on how to contribute.
The MIT License (MIT). Please see LICENSE for more information.