Install
$ agentstack add skill-jeffsenso-prestashop-skills-create-controller-form-actions ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
create-controller-form-actions
Read @.ai/Component/Controller/CONTEXT.md for controller conventions (base class, DI, security attributes, error mapping). Read @.ai/Component/Forms/CONTEXT.md for form patterns (FormBuilder, FormHandler, FormDataProvider, FormDataHandler).
1. Create action
FormBuilderInterface and FormHandlerInterface are injected as action arguments (preferred DI mode — see Controller/CONTEXT.md), each with an explicit #[Autowire(service: '…')] so the action picks up the domain-specific form services:
#[AdminSecurity("is_granted('create', request.get('_legacy_controller'))")]
public function createAction(
Request $request,
#[Autowire(service: 'prestashop.core.form.identifiable_object.builder.{domain}_form_builder')]
FormBuilderInterface ${domain}FormBuilder,
#[Autowire(service: 'prestashop.core.form.identifiable_object.handler.{domain}_form_handler')]
FormHandlerInterface ${domain}FormHandler,
): Response
- GET: build form via
FormBuilderInterface::getForm(), render form template - POST: handle request via
FormHandlerInterface::handle()— it internally callsFormDataHandler::create()which dispatches the Add command - Never instantiate commands directly in the controller
- On success: flash message + redirect to index or edit page (return the new entity ID via
getIdentifiableObjectId()) - On failure: re-render form with errors
Reference: TaxController::createAction(), ManufacturerController::createAction()
2. Edit action
Same injection mode — FormBuilderInterface and FormHandlerInterface as action arguments:
#[AdminSecurity("is_granted('update', request.get('_legacy_controller'))")]
public function editAction(
Request $request,
int ${domain}Id,
#[Autowire(service: 'prestashop.core.form.identifiable_object.builder.{domain}_form_builder')]
FormBuilderInterface ${domain}FormBuilder,
#[Autowire(service: 'prestashop.core.form.identifiable_object.handler.{domain}_form_handler')]
FormHandlerInterface ${domain}FormHandler,
): Response
- GET:
${domain}FormBuilder->getFormFor(${domain}Id)—FormDataProvider::getData($id)is called internally to pre-fill the form - POST:
${domain}FormHandler->handle($form)— callsFormDataHandler::update()which dispatches Edit command - Catch
{Domain}NotFoundException→ error flash, redirect to index (entity may have been deleted concurrently)
Reference: TaxController::editAction(), ManufacturerController::editAction()
3. Entity-specific actions
Some entities require additional actions beyond create/edit:
- Image deletion:
deleteCoverImageAction(int $id) - Export:
exportAction(Request $request)— generates CSV/PDF - View page:
viewAction(int $id)— read-only detail page (e.g. Customer, Order) - Position update:
updatePositionAction(Request $request)— handled byPositionDefinition
For these single-purpose actions, skip FormBuilder/FormHandler — they exist to bridge a Symfony form lifecycle to a CQRS command, which is overkill for an action that just dispatches one command or query. Inject the bus (or specific handler) and dispatch directly:
#[AdminSecurity("is_granted('delete', request.get('_legacy_controller'))")]
public function deleteCoverImageAction(int ${domain}Id): RedirectResponse
{
try {
$this->dispatchCommand(new DeleteCoverImageCommand(${domain}Id));
$this->addFlash('success', /* … */);
} catch ({Domain}NotFoundException $e) {
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
}
return $this->redirectToRoute('admin_{domain}s_edit', ['{domain}Id' => ${domain}Id]);
}
The FormBuilder/FormHandler pair is required for the create/edit form lifecycle — not for every controller action.
Rules
- See
@.ai/Component/Controller/CONTEXT.mdfor all controller conventions (DI order, security attributes, error mapping) - Create/edit go through FormBuilder/FormHandler — never build the create/edit commands manually
- Single-purpose actions (toggle, delete sub-resource, export, view) dispatch commands/queries directly
- FormDataProvider transforms query results into form-ready data, not the controller
- Catch NotFoundException on edit — the entity may have been deleted between page load and save
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jeffsenso
- Source: jeffsenso/prestashop-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.