Skip to content
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

Finish PHP extenders #1891

Closed
78 tasks done
franzliedke opened this issue Sep 23, 2019 · 12 comments
Closed
78 tasks done

Finish PHP extenders #1891

franzliedke opened this issue Sep 23, 2019 · 12 comments

Comments

@franzliedke
Copy link
Contributor

franzliedke commented Sep 23, 2019

Since introducing the extender API in beta.8, a bit of time has passed and we still need to add extenders for the following use-cases.

This list should cover what we need to implement all official, bundled extensions' backends only using existing extenders.

Remaining Post-Beta 15 Steps:

  • Finish up and merge Search extenders
  • Review and merge user password checker extenders
  • Look over extender naming (perhaps mutate => attributes for ApiSerializer?)
  • Add a 4th argument default value to Setting extender
  • Add a beforeSending callback method to Notification extender
  • Figure out what to do with GetModelIsPrivate
  • Add subscribe to event extender, but register subscribers only after application is fully booted to avoid early resolution.

Extenders To Replace Events

At this stage, our goal is to replace the current event-based extension system for non-domain events, not to introduce new extensibility functionality (except where we can break current ambiguous events into more granular, use-driven extenders).

Events to Drop

  • Event\GetModelIsPrivate (The Saving event for Post / Discussion gets the job done more simply)

Events To Keep

Discussion

  • Discussion\Event\Deleted
  • Discussion\Event\Deleting
  • Discusion\Event\Hidden
  • Discussion\Event\Renamed
  • Discussion\Event\Restored
  • Discussion\Event\Saving
  • Discussion\Event\Started
  • Discussion\Event\UserDataSaving
  • Discussion\Event\UserRead

Extension

  • Extension\Event\Disabled
  • Extension\Event\Disabling
  • Extension\Event\Enabled
  • Extension\Event\Enabling
  • Extension\Event\Uninstalled

Group

  • Group\Event\Created
  • Group\Event\Deleted
  • Group\Event\Deleting
  • Group\Event\Renamed
  • Group\Event\Saving

Post

  • Post\Event\Deleting
  • Post\Event\Deleted
  • Post\Event\Hidden
  • Post\Event\Restored
  • Post\Event\Revised
  • Post\Event\Saving

Settings

  • Settings\Event\Saved
  • Settings\Event\Saving
  • Settings\Event\Deserializing
  • Settings\Event\Serializing

User

  • User\Event\Activated
  • User\Event\AvatarChanged
  • User\Event\AvatarDeleting
  • User\Event\AvatarSaving
  • User\Event\BioChanged
  • User\Event\Deleted
  • User\Event\Deleting
  • User\Event\EmailChanged
  • User\Event\EmailChangeRequested
  • User\Event\GroupsChanged
  • User\Event\LoggedIn
  • User\Event\LoggedOut
  • User\Event\Registered
  • User\Event\RegisteringFromProvider
  • User\Event\Renamed
  • User\Event\Saving

OLD LIST

Most of them come from Toby's original gist.

Other related issues: #1223

@franzliedke franzliedke added this to the 0.1.0-beta.11 milestone Sep 23, 2019
@franzliedke franzliedke pinned this issue Sep 23, 2019
@luceos
Copy link
Member

luceos commented Sep 25, 2019

Looking at your list I am unsure how you want to move forward. It feels like adding extenders for all of them feels like we're bloating core. We could potentially even make the Events themselves smarter to allow for easier extensibility.

Looking at Toby's gist I see a lot of possible unnecessary duplication, eg:

new Extend\DiscussionGambit(TagGambit::class),

Why not:

new Extend\Gambit(Discussion::class, TagGambit::class),

Maybe I'm completely oblivious to the exact plans.

@franzliedke
Copy link
Contributor Author

It feels like adding extenders for all of them feels like we're bloating core.

I think the more focused public API and the greater freedom to make internal changes without having to change the API for extensions is absolutely worth it. Once we have the public API, we can get rid of the events as implementation detail in many cases, which should pretty much reduce the bloat by the same amount we increased it.

We could potentially even make the Events themselves smarter to allow for easier extensibility.

Not sure I understand - can you expand on this? Maybe with an example?

Looking at Toby's gist I see a lot of possible unnecessary duplication

Yeah. We can probably implement them as one. Unless something comes up that shows why Toby proposed them the way he did. 😉

@luceos
Copy link
Member

luceos commented Sep 26, 2019

Here's an example from tenancy where we've added a few helper methods on the event directly to simplify implementation for the developer:

use Illuminate\Routing\RouteCollection;
use Illuminate\Routing\Router;
use Tenancy\Identification\Events\Switched;

class ConfigureRoutes
{
    /**
     * @var Switched
     */
    public $event;

    /**
     * @var Router
     */
    public $router;

    public function __construct(Switched $event, Router $router)
    {
        $this->event = $event;
        $this->router = $router;
    }

    /**
     * Flush all tenant routes for this request.
     *
     * @return $this
     */
    public function flush()
    {
        $this->router->setRoutes(new RouteCollection());

        return $this;
    }

    /**
     * Adds routes from a routes.php file to the current request.
     *
     * @param array  $attributes
     * @param string $path
     *
     * @return $this
     */
    public function fromFile(array $attributes, string $path)
    {
        $this->router->group($attributes, $path);

        return $this;
    }
}

@cmcjacob
Copy link

cmcjacob commented Oct 14, 2019

I think there's a heavy boilerplate with the current state of relying so much on event listeners. They are implemented well, but come with a degree of importance that it can give the impression they
are the only option going forward.

Most of the helpers in this list sound beneficial as extenders, but it seems like some of them should be decoupled as officially enabled extensions, so kind of confused how core would be considered bloated from a supplicant feature.

For example, why are there so many Post listeners described here? If all listeners of one type are handled in these extender helpers, one would expect other events to be as well be (unless I'm not visualizing the implementation correctly).

Furthermore, if (hypothetically) 90% of events are left as blank containers and the other 10% are enhanced with app/logic helpers - could provide an unwanted layer of inconsistency that doesn't contrast well with the existing extender methods. I can understand why it's easier, afterall many of these helpers will rely on events themselves, but I also think these extenders would be more the more appealing option because of simplicity in design.

@franzliedke
Copy link
Contributor Author

@cmcjacob Most extenders will rely on the existing events for implementation in the beginning. But as soon as the extenders are there, extension developers will be expected to use those - events will then be considered private API.

As you mention, some of the events are quite clunky for some of their use-cases, which is why we will likely change the implementation of extenders to something more direct (e.g. storing an array of middleware per "frontend" in the IoC container directly).

@luceos luceos modified the milestones: 0.1.0-beta.12, 0.1 Dec 6, 2019
@franzliedke
Copy link
Contributor Author

For my own reference: I noticed that this GitHub code search is very useful to find out what extension developers are currently building custom extenders for. 😃

@askvortsov1
Copy link
Member

Please forgive me if this question is a bit uninformed, but is the goal of this to shift the vast majority of extension-flarum interaction to the extender interface (in effect, removing the necessity of listening into events)?

Also, if that's the case, I noticed that there's a lack of extenders that have to do with logging in / registering. There are valid cases for actions to be executed after login (see https://github.com/askvortsov1/flarum-auth-sync), so I think that's something we should plan for.

Essentially, I'm wondering how the extenders to have were selected, and to what extend this list is set in stone?

@franzliedke
Copy link
Contributor Author

is the goal of this to shift the vast majority of extension-flarum interaction to the extender interface (in effect, removing the necessity of listening into events)?

Yes, pretty much. Whether they use events internally or not, is going to be an implementation detail and considered private API. Basically, the extenders (and what other classes the extenders reference and expose) will make up the public API of Flarum's backend.

Essentially, I'm wondering how the extenders to have were selected...

Basically, I want all bundled extensions to be implemented fully in terms of extenders. They cover a variety of use-cases (experimenting with those use-cases was one of the reasons why the extensions were implemented so early), so we are hoping to cover enough ground with these. Additional input / requests on top are, of course, welcome, but I'd consider them in nice-to-have category.

...and to what extend this list is set in stone?

It's not, see above. Especially the exact interfaces of the concrete extenders are up to discussion - best kept to concrete sub-tickets, though.

Baseline for discussion would be this Gist by Toby, Flarum's founder, where he envisioned what the tags extension would look like if implemented using extenders.

@franzliedke
Copy link
Contributor Author

P.S.: I want extensions to mostly use extenders, not build their own. The latter has become a bit too common for my personal taste, but that's mostly due to our slow pace in creating more official extenders. 😉

@askvortsov1
Copy link
Member

It's not, see above. Especially the exact interfaces of the concrete extenders are up to discussion - best kept to concrete sub-tickets, though.

I think that to organize work on this, it might be best to start this discussion and decide which ones we definitely want, as well as any kind of priority we want to do these in.

@franzliedke
Copy link
Contributor Author

We'll organize a call / chat meeting in early March, after beta.12 is released. Everybody who's interested, let us know and we'll give you a chance to join.

@askvortsov1
Copy link
Member

askvortsov1 commented Mar 9, 2020

For the event listener ones, what if we refactored all the created/deleted/saving/etc events out into model-agnostic ones, and created a general use AbstractLifecycleHandler, which extension devs could override and register via Extend\Model->addLifecycleHandler?

The lifecycle handler could have methods ie onDeleting, onDeleted, onCreating, on Created, etc which, if defined by extension devs, would be subscribed onto the new model-agnostic events.

This should simplify/add flexibility to the extender system, allow the per-model extenders (e.g. the User or Group or Post or Notification extenders) to focus on model-specific situations, and also fix #1223.

Additionally, models could define the set of events that they support (e.g. User currently has no soft delete), which would tie everything together brilliantly.

Additionally, do we want to somehow validate input into extender methods?

@askvortsov1 askvortsov1 self-assigned this Oct 28, 2020
@askvortsov1 askvortsov1 unpinned this issue Feb 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants