-
-
Notifications
You must be signed in to change notification settings - Fork 35
Allow scoped and local extensions #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This allows one to create either a scoped global extension through "static::extend", which will apply to all newly-constructed instances of a class going forward, or they can also use "$object->extend" to apply an extension to the single instance. This allows access to protected and private methods within an instance, allowing full customisation of a given instance if it's allowed to be extendable.
|
|
Could you add some usage examples? |
|
@LukeTowers sure, so one example, which spurned this PR, was me trying to emulate the Since the behavior is provided the controller as a For example - this won't work: public function apiRun(string $action, array $params = [])
{
$this->controller->action = $action;
$this->controller->params = $params;
$this->controller->suppressView = true;
// Determine if the action is public
$isPublicAction = in_array($action, $this->controller->publicActions);
if (!$isPublicAction) {
if ($this->controller->requiredPermissions && !$this->controller->user->hasAnyAccess($this->controller->requiredPermissions)) {
return $this->errorResponse('Unable to access this API endpoint', 403);
}
}With this PR in place, I can use a local extension and return the callback to determine authorisation: public function apiRun(string $action, array $params = [])
{
$this->controller->action = $action;
$this->controller->params = $params;
$this->controller->suppressView = true;
// Determine if the action is public
$allowed = $this->controller->extend(function () {
$isPublicAction = in_array($action, $this->publicActions);
if (!$isPublicAction) {
if ($this->requiredPermissions && !$this->user->hasAnyAccess($this->requiredPermissions)) {
return false;
}
}
return true;
});
if (!$allowed) {
return $this->errorResponse('Unable to access this API endpoint', 403);
} |
|
Another example, say I wanted to make a global extension, but needed access to some protected methods or properties, this would now be possible: FormWidgetBase::extend(function () {
// $valueFrom is protected, and has no getter in this class.
if ($this->valueFrom === 'my_field') {
$this->addDynamicMethod('someMethod', function () {
// ....
});
}
}, true); |
|
Just realised in the most recent tests that this also means that we can now define dynamic methods that can use protected or private methods and properties within. |
|
Hmm, with that example I'm not so sure that extend is the right name for that method, at least in the context of the use case of injecting the provided closure into the context of the object instance that |
|
@LukeTowers the name can certainly be worked on if there's any better ideas, although My first example is an example of a global extension (ie. |
|
@LukeTowers any further thoughts on the above? |
|
Tested this with my use case where I need to have access to methods added by behavior and it works beautifully: And it's amazingly nice to be able to use |
|
This is so freaking powerful!!! This changes everything. Amazing work @bennothommo! |
|
What's also very nice is behavior is unchanged if before: after: |
|
Let's get the conflicts resolved and a quick docs PR up and then I can review |
|
Ready to go @LukeTowers |
mjauvin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, can't wait to use this!
@bennothommo I successfully tested this before the PR got merged, but now it doesn't see the behavior methods... |
|
@mjauvin I'd need to see the context of how you've implemented the extension. |
|
@bennothommo here is an example: public function boot(): void
{
\StudioAzura\Aerocom\Models\Service::extend(function () {
$this->extend(function () {
$this->addTranslatableAttributes('myTranslatableField');
});
}, true);
}The Service model implements the TranslatableModel behavior and should have the Call to undefined method StudioAzura\Aerocom\Models\Service::addTranslatableAttributes() I tested this exact case before the PR got merged, so I know it was working. What am I missing ? |
|
@mjauvin I'm not sure how it worked before 😕. Basically, the extension callbacks (ie. the static |
|
Wait - I saw my comment in your PR that was to introduce the |
|
@mjauvin This commit should fix it (a8ba4ee), however, I'm still curious as to how it worked before, because it should never have. Static The commit above will delay local extensions until after the construction is complete, because that was always the intention of them to be available on "constructed" objects (like when used in run-time). |
|
I don't know, but I'm 99% sure I tested this when I wrote the comment in this PR. Maybe I did something stupid thinking it worked |
|
@bennothommo the problem is still there, behavior method is still not found. |
|
@mjauvin are you sure? The new test case I introduced in that commit is testing your exact scenario? |
|
@bennothommo pretty sure. Where is |
|
@bennothommo problem is your test class extends the Extendable class, mine extends the Model class (which uses the ExtendableTrait) |
This allows one to create either a scoped global extension through
static::extend(), which will apply to all newly-constructed instances of a class going forward, or they can also use$object->extend()to apply an extension to the single instance at run-time.This allows access to protected and private methods within an instance, allowing full customisation of a given instance if it's allowed to be extendable.