68publishers / smart-nette-component
Features for Nette Components and Presenters. Authorization via annotations, template resolving and overloading etc. ...
Package info
github.com/68publishers/smart-nette-component
pkg:composer/68publishers/smart-nette-component
Requires
- php: ^8.3
- nette/application: ^3.1.0
- nette/di: ^3.0.10
- nette/security: ^3.1.4
- nette/utils: ^3.2 || ^4.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.89
- latte/latte: ^2.5 || ^3.0
- mockery/mockery: ^1.6.12
- nette/bootstrap: ^3.1
- nette/robot-loader: ^3.4 || ^4.0
- nette/tester: ^2.6
- phpstan/phpstan: ^2.1
- phpstan/phpstan-nette: ^2.0
- roave/security-advisories: dev-latest
Suggests
- nette/robot-loader: Required for the option `cache: yes` with `scanDirs` of ComponentAuthorizationExtension.
Provides
None
Conflicts
- nette/component-model: <3.0.2
Replaces
None
This package is auto-updated.
Last update: 2026-10-28 15:09:00 UTC
README
This package adds some useful features for Nette Presenters and Components, such as authorization for Presenters, their actions and signals using PHP8 attributes, authorization for component signals using attributes, and resolving of component template files.
Installation
The best way to install 68publishers/smart-nette-component is using Composer:
$ composer require 68publishers/smart-nette-component
The package requires PHP ^8.3 and nette/application ^3.1.0.
The option scanDirs of the compiler extension (with cache: yes) requires nette/robot-loader (^3.4 || ^4.0).
Authorization attributes
To use the authorization attributes, you need to register a compiler extension.
extensions: component_authorization: SixtyEightPublishers\SmartNetteComponent\Bridge\Nette\DI\ComponentAuthorizationExtension # The default configuration (you don't need to define it) is as follows: component_authorization: cache: %debugMode% scanDirs: - %appDir% scanComposer: yes scanFilters: - *Presenter - *Control - *Component
Attributes can be cached when folding the DI container to avoid using reflection at runtime.
The extension will create a classmap and a map of all attributes automatically, it just needs to know where to look for Presenters and Components.
This is done with the scanDirs, scanComposer and scanFilters options, which behave similarly to nette/application.
Now add the following trait to your BasePresenter and BaseControl:
use Nette\Application\UI\Presenter; use SixtyEightPublishers\SmartNetteComponent\Bridge\Nette\Application\AuthorizationTrait; abstract class BasePresenter extends Presenter { use AuthorizationTrait; }
use Nette\Application\UI\Control; use SixtyEightPublishers\SmartNetteComponent\Bridge\Nette\Application\AuthorizationTrait; abstract class BaseControl extends Control { use AuthorizationTrait; }
From now, you can use authorization attributes in your Presenters and Components:
use SixtyEightPublishers\SmartNetteComponent\Attribute\LoggedIn; use SixtyEightPublishers\SmartNetteComponent\Attribute\Allowed; #[LoggedIn] final class AdminProductPresenter extends BasePresenter { #[Allowed('product_resource', 'add')] public function actionAdd(): void {} #[Allowed('product_resource', 'delete')] public function handleDelete(): void {} }
use SixtyEightPublishers\SmartNetteComponent\Attribute\LoggedIn; use SixtyEightPublishers\SmartNetteComponent\Attribute\Allowed; final class EditOrderControl extends BaseControl { #[Allowed('order_resource', 'delete_item')] public function handleDeleteItem(): void {} }
The Presenter/Component throws the exception SixtyEightPublishers\SmartNetteComponent\Exception\ForbiddenRequestException if any of the conditions in the attributes are not met.
The package includes the following attributes:
AllowedInRoleLoggedInLoggedOut
If you would like to react somehow to the thrown exception, you can overwrite the onForbiddenRequest() method in a Presenter/Component.
protected function onForbiddenRequest(ForbiddenRequestException $exception): void { # `$exception->rule` contains failed attribute $this->flashMessage('You don\'t have access here!', 'error'); $this->redirect('Homepage:'); }
Custom authorization attributes
You can register your own attributes in the following way:
use SixtyEightPublishers\SmartNetteComponent\Attribute\AttributeInterface; use SixtyEightPublishers\SmartNetteComponent\Authorization\RuleInterface; use SixtyEightPublishers\SmartNetteComponent\Authorization\RuleHandlerInterface; use SixtyEightPublishers\SmartNetteComponent\Exception\ForbiddenRequestException; final class CustomRule implements AttributeInterface, RuleInterface { # ... } final class CustomRuleHandler implements RuleHandlerInterface { public function canHandle(RuleInterface $rule): bool { return $rule instanceof CustomRule; } public function __invoke(RuleInterface $rule): void { assert($rule instanceof CustomRule); if (...) { throw new ForbiddenRequestException($rule); } } }
services: - autowired: no factory: CustomRuleHandler
Custom component authorizator
If you replace the service ComponentAuthorizatorInterface with your own implementation, note that checkAction() receives the action name (e.g. default for actionDefault()) and checkSignal() receives the signal name (e.g. delete for handleDelete()), not the method names.
Template resolving
You don't need to register any compiler extension to use this feature, just use the TemplateResolverTrait trait in your BaseControl.
use Nette\Application\UI\Control; use SixtyEightPublishers\SmartNetteComponent\Bridge\Nette\Application\TemplateResolverTrait; abstract class BaseControl extends Control { use TemplateResolverTrait; }
The base render() method is already declared in the trait.
To assign variables to the template, we can use the beforeRender() method.
You can also define custom render*() methods that are called for rendering using {control myControl:foo}.
final class MyControl extends BaseControl { protected function beforeRender(): void { # assign variables into the template here } public function renderFoo(): void { $this->doRender('foo'); } }
The template for the base render method will be resolved as COMPONENT_DIRECTORY/templates/myControl.latte or COMPONENT_DIRECTORY/templates/MyControl.latte.
The template for the foo render method will be resolved as COMPONENT_DIRECTORY/templates/foo.myControl.latte or COMPONENT_DIRECTORY/templates/foo.MyControl.latte.
Of course, you can set a template file manually:
final class MyPresenter extends BasePresenter { protected function createComponentMyControl() : FooControl { $control = $this->myControlFactory->create(); $control->setFile(__DIR__ . '/path/to/file.latte'); # or relatively from a component's directory: $control->setRelativeFile('templates/new/myControl.latte'); # you can change the template for a specific render type: $control->setFile(__DIR__ . '/path/to/myControl.latte', 'foo'); # template for `renderFoo()` return $control; } }
Upgrade
1.x → 2.0
- The minimal PHP version is 8.3.
- Security fix: in 1.x, the authorization attributes on action methods (
action<Action>()) and signal methods (handle<Signal>()) were never enforced.AuthorizationTraitpassed the method name (e.g.actionDefault) toComponentAuthorizatorInterface::checkAction()/checkSignal()and the rules provider looked up the methodactionActionDefault(), which doesn't exist, so no rules were found. Since 2.0 the rules are checked - review the attributes on your action and signal methods (and your rule handlers), requests that were allowed until now may be forbidden. - ❗
AuthorizationTraitpasses the action name (default) and the signal name (delete) toComponentAuthorizatorInterface::checkAction()andcheckSignal()instead of the method names (actionDefault,handleDelete). Update your ownComponentAuthorizatorInterfaceimplementations if you have any.
Contributing
Before opening a pull request, please check your changes using the following commands
$ make init # to pull and start all docker images
$ make cs.check
$ make stan
$ make tests.all