Skip to content

Development

Overview

The package contains four main features:

  • Stubbing files and folders for new portal projects
  • Providing a set of Ready-To-Go Livewire components
  • Providing a clean set of routes for Authorization features
  • Providing the Statamic CMS, and entries module for content management.

Because of this, the project is spilt into different sections.

FeatureFolder
Package setup./src
Application code./src/Integrations/<type>
Application stubs./resources/stubs/application/<type>
Portal Livewire views./resources/views/portal/livewire

The package currently supports three different types of Applications, Statamic cms, Portal, or Website.

Package setup: Contains the code needed to setup a Laravel package. It loads service providers, registers commands, and creates blade/livewire namespaces.
Application code: Contains features for application types. This includes authorization code for the portal, integration for the CMS, or Utilities for Project development,
Application stubs: Contains stubs files that copied to a project when qore-frontend::initialize is run. This gives developers a starting point when designing pages, or creating features.
Portal Livewire views: Contains view files used by Livewire components that can be found in the Portal Application Code. These files are namespaced to qore-portal::livewire.<component>;

Developing features guidelines

When developing new features, documentation is essential. Features that are created, but do not exist in the documentation might aswell not exists at all.
Searching through the source code of a package hoping a feature already exists is tedious, and often leads to developers writing the feature themselves.
WRITE DOCUMENTATION
Add snippets for easy useage, explain what exists and what doesn't. Add fixed for common mistakes or error messages. Anything (non-ai) is better than having nothing.

Limited configuration

Previously we added everything and anything to the config file of the Application. Good idea for flexibility, terrible idea because it makes the config unreadable.
With the lack of documentation, no-one knows what config field does what, or where its used. On top of that the code becomes harder to maintain and read, see exhibit A).
Long function names, fetching redirects from a config, defaulting to some other value. That default, is not the default value btw, its the key used to fetch the name of named routes, and if that fails a third, hidden default is used. Not the best.

php
class RegisterResponse implements RegisterResponseContract
{
    /**
     * Create an HTTP response that represents the object.
     *
     * @param  Request  $request
     */
    public function toResponse($request): Response|Redirector
    {
        return $request->wantsJson()
            ? new JsonResponse('', 201)
            : redirect()->intended(
                route(
                    ApplicationFeatures::currentApplicationEmailVerification() ?
                                Qore::currentApplicationConfigRedirect('register', 'verification.notice') :
                                Qore::currentApplicationConfigRedirect('register', 'dashboard'))
            );
    }
}

Documentation with examples that can be copied to add a feature, or to configure a feature are easier to maintain, and easier to encounter while coding. I have no idea what Qore::currentApplicationConfigRedirect('register', 'verification.notice')does exactly, but seeing a line with QoreFrontendRouting::aliasPortalMiddleware($middleware); allows me to click on the function definition and found out. The later takes a single click, the former one click to open the function, and 2-4 more including manually opening a config file.

The same goes for the routes file, QoreFrontendRouting::legacyRoutes($this->application); is one click away from knowing what is going on. The previously used $app->config('classes.middleware.authenticated') . ':' . $app->config('guard') is not. Or a less nitpicked example, ['middleware' => ['qore-portal-auth']]

Configure through docs and code, not 100+ lines of undocumented views, redirects and routes lists.

Use livewire instead of Http/Controllers

Previously we used GET and POST controllers with the same address and route name to create features. Interactivity on pages was very limited in this way. Whenever possible create reusable actions instead of using a single purpose controller. Having an action that can be reused regardless of context (controller, livewire component, job, command etc.) is preferable, and provides better flexibility of projects.
Some guidelines to take into account:

  • Don't use request() directly, it makes the code very dependant on the incoming request and does not work with livewire. Use function params or data objects instead.
  • Return action results instead of native php values. This allows actions to return values, errors, or redirects without having to specify how any of those are handled.
  • Avoid throwing exceptions for errors, instead add them to a list and return them from the action. Ofcourse if the code should error (because of an unhandelable case), do throw exceptions but add these to the function declaration.
    • previously controllers relied on the exception handeling of laravel to convert thrown exceptions to json responses. Very hidden functionality, not easily portable to other environments.

Maintainability in mind

Stubs are great for deploying code to a project. The main downside however, you can't easily update projects with new versions of fixes to the stubs. Once the stubs are deployed, and possibly edited, merging the two differences becomes a pain.

When creating new features, take a moment to concider if this should only be deployed once, or updates might be required later on. Livewire components are placed in the Integration folder for this purpose, instead of being deployed. Being able to update the logic, and actions being used across projects by updating this package is easier than manually comparing all stubbed files for important changes. There is one downside to Integration code however, what if a project extends the functionality and the upgrade breaks the project in a way we weren't expecting?

Extending existing features moves the code from package to project, which means is should be included in the projects testing. Ofcourse this doesn't always happen, so we should plan for cases where the code wasn't properly tested. In comes the next guide line.

Write change logs

We don't always know what was in a commit or update. Not all commits are tagged with the story they were created for, and even so, that doesn't help all that much when there are 10+ commits. Knowing what features changes are in an update, what breaking changes where made, or what breaks to look out for, is easier than blindly and manually updating. New Features and changelogs should include the following items:

  • New features, including docs.
  • Changes
  • Breaking changes + Deprecated features.

Having an overview of there should help with updating this package in projects, including projects with limited testing.
Please include these in PRs!

Note: I still want to implement a script that takes a list of changelog files and combines them into a changelog when creating a new tagged version release. Semversioning is already used for the version numbers, including this version number in a MD file in the docs would be great.