Actions define the behavior of forms.
Map the action name to the action controller in your plugin's actions.php
:
namespace My\Namespace\Actions; // actions.php return array( 'do/work' => DoWork::class, );
Define a class that extends Elgg\Actions\Action
and implement the execute()
function:
namespace My\Namespace\Actions; use Elgg\Actions\Action; use Elgg\Actions\Response; use Elgg\Actions\Input; use Elgg\Some\Service; class DoWork extends Action { public function __constructor(Service $someService) { $this->theService = $someService; } public function execute(Input $input) { var $result = $this->theService->doWork(); return new Response(200, ['result' => $result]); } }
Note
Elgg's DI system will inject dependencies into the constructor automagically.
Write a unit test:
namespace My\Namespace\Actions; use Elgg\Actions\Input; use Elgg\Some\Service; class DoWorkTest extends PHPUnit_Framework_TestCase { public function testRespondsWithTheResultOfServicesDoWorkMethod() { $serviceMock = $this->createMock(Service::class); $action = new DoWork($serviceMock); $response = $action->execute(new Input()); $this->assertEquals(200, $response->httpStatus); $this->assertEquals('foo', $response->body->result); } }
A single action representing the behavior of a single form. Noop by default.
Called during permissions phase to check whether the current session can perform the requested action.
Return the complete list of inputs this action accepts along with the validation rules.
Contains the business logic for the action.
namespace Elgg\Users\Actions; use Elgg\Actions\NullAction; // Potential implementation of the "register" action class Register extends NullAction { // primitives injected automatically from $config service public function __constructor($allow_registrations, Users $users, Passwords $passwords, Notifications $notifications) { $this->allow_registrations = $allow_registrations; $this->users = $users; $this->passwords = $passwords; $this->notifications = $notifications; } public function getFields() { return array( "email" => input()->type('email')->validate('unique', function($value, Users $users) { return count($users->where(['email' => $value])->ignoreAccess()) == 0; }), "name" => input()->type('text'), // input field validation is transferable knowledge because it's based on HTML5 "username" => input() ->type('text') ->pattern('[a-zA-Z0-9_.-]+') ->minlength(4) // named validation rules allow for custom error messages when rules are broken // Return true for valid input. False for invalid input. // This callback is dependency-injected including with the value of the input ->validate('unique', function($value, Users $users) { return count($users->where(['username' => $value])->ignoreAccess()) == 0; }), "password" => input()->type('password')->minlength(6), "password2" => input()->type('password')->validate('matches', function($value, Input $input) { return $value == $input->get('password'); }), "friend" => entity()->type('user')->required(false), "invitecode" => input()->type('hidden')->required(false) ); } public function hasPermission(Input $input) { return $this->allow_registrations; } public function execute(Input $input) { $user = $this->users->create([ 'username' => $input->get('username'), 'email' => $input->get('email'), 'name' => $input->get('name'), 'access_id' => ACCESS_PUBLIC, 'owner_guid' => 0, 'container_guid' => 0, 'language' => $this->language, ]); $this->passwords->set($user->guid, $input->get('password')); // Turn on email notifications by default $this->notifications->setUserSetting($user->guid, 'email', true); return new EntityResponse($user); } }
Tweak the inputs and behaviors of actions with Action Decorators.
namespace Elgg\Actions; class ActionDecorator implements Action { private $decoratedAction; public function __construct(Action $decoratedAction) { $this->decoratedAction = $decoratedAction; } public function getFields() { return $this->decoratedAction->getFields(); } public function execute(Input $input) { return $this->decoratedAction->execute($input) } }
namespace Elgg\Captcha\Actions; use Elgg\Actions\Action; use Elgg\Actions\ActionDecorator; use Elgg\Inputs\InputField; /** * Validates that the user filled out a captcha before submitting */ class CaptchaActionDecorator extends ActionDecorator { // Must be named exactly "decoratedAction" to inject correctly public function __construct(Action $decoratedAction, Session $session) { parent::__construct($decoratedAction); $this->session = $session; } // Change behavior of injected action by overriding methods public function getFields() { // Never make logged in users fill out captcha if ($this->session->isLoggedIn()) { return parent::getFields(); } // Utilize existing behavior by calling parent methods return array_merge(parent::getFields(), array( 'captcha' => input()->type('captcha')->options([3]), )); } }
namespace Elgg\Captcha\Actions; // actions.php return array( "users/register" => array( Decorator::class => 500, ), );
use Elgg\Captcha\Actions\Decorator as CaptchaDecorator; // actions.php return array( "users/register" => array( CaptchaDecorator::class => false, ), );
- Parse the action name from the URL
- Get the action configuration based on the name
- Instantiate the default action class
- Instantiate decorators
- Validate inputs (Your getFields() is called here)
- Check permissions (Your hasPermission() is called here)
- Execute the action (Your execute() is called here)
- Render response
When the http://example.elgg.org/action/users/register endpoint is hit, this conceptually calls:
$action = new RegisterUser(); // priority 0 $action = new CaptchaDecorator($action); // priority 500 // validate, check permissions, etc... $action->execute($input);
Except there is actually some DI magic sprinkled in to make sure the constructors receive all the correct arguments.
I'm seeing just the raw text. Are you sure the file language has been set to rst?