CrestApps / laravel-code-generator

An efficient Laravel code generator, saving time by automating the creation of resources such as views, controllers, routes, migrations, languages, and form-requests. Highly flexible and customizable, it includes a cross-browser compatible template and client-side validation for application modernization.
https://laravel-code-generator.crestapps.com
MIT License
737 stars 158 forks source link

Using LCG with Laravel-Modules #105

Open larry-tx opened 5 years ago

larry-tx commented 5 years ago

Environment:

Description:

Modular Development

I am working on a very complex web application. In order to make it more manageable, I've chosen to go with a modular system as implemented by laravel-modules I certainly don't expect you to accommodate a package by another author, modularity has become one principal way to use any framework in a complex application. Hopefully, you'll be able to give me some guidance in making use of your outstanding package.

I'm sure that CrestApps is familiar with modular development, but please indulge me a bit of explanation to avoid any confustion. A module should be entirely self-contained. That is a module should encapsulate its own migrations, models, controllers, view, routes, etc. If someone wanted to use a module that I created in their Laravel app, I should be able to simply copy a module folder from my app (e.g., «ProjectRoot»\Modules\Network) and hand it off to the other person. That person would simply paste the module folder in their application, run the module-specific migration found, not in «ProjectRoot»\Modules\Network\Database\Migrations but in «ProjectR00t»\Modules\Network\Database\Migrations. Of course any given module could easily contain many models and associated controllers, views groups and so on.

One of my modules that I repeatedly reuse is Common which ontains the associated models, controllers, etc. for these tables, among ten others others:

Because the data from these tables needs to "migrate" with the module, I've modified my migrations for the module to not only create the associated tables bu to also insert the data.

Basically, where I'm having difficulty is in the location and namespacing of some of the files generated by LCG. The structure of my modular applications looks like this:

Thus, for each module (using here, Network as one example, of many, many modules) has a structure that parallels the whole project structure.

Hopefully, these example make it clear why modules are absolutely essential to any Web app that is beyound the merely trivial

Because of the way that Laravel handles resources, begrudgingly I've chosen to put my views under «ProjectRoot»\resources\views instead of the «ProjectRoot»\Modules\«ModuleName»\resources\views path. I'm entirely negotiable on that point. Actually, as with most most modular development, I place the primary source views files under the Module/«ModuleName»/resources/views directory in order to maintain the basic principles of modularity and "publish" them to the «ProjectRoot»/resources/views in order to accommodate Laravel.

CodeGenerator and Modular Development

I've tried using the published user-modifications for CodeGenerator to achieve the principles of modular development.

For instance, CodeGenerator put the principle files (model, controller, request) in the path «ProjectRoute»\app\Modules\Network\Entities, «ProjectRoute»\app\Modules\Network\Http\Controllers, etc. rather than «ProjectRoute»\Modules\Network\Entities despite the fact that the custom config file had 'models_path' => '/Modules/Network/Entities',. It would appear that the package code has something like app_path( '/Modules/Network/Entities') rather than base_path( '/Modules/Network/Entities') to compute the location of the models.

Other (mostly non-modular but related issues

My routes are a little more complex. I don't like the use of the "admin" sub-directory, preferring instead to simply specify the route with an admin group. For instance, I know that my Network module will always be within the admin route group with associated permissions. On the other hand, the index and show views of portions of my HomeAutomation module will not be under the admin route group while the create and update views will be.

Actually, I prefer a routing group/sub-group(s) structure like this:

Route::group(['prefix' => 'admin'], function() {
    Route::group(['prefix' => 'network], function() {
        Route::resource('devices', \Modules\Network\NetworkDevicesController);
    });
});

To produce a URL like:

    https://example.com/admin/network/devices/index

The beauty of routing is that I can discard th reference to "Modules" (important for the developer but totally irrelevant to the user) and the duplicative "network" in "network_devices" (important in keeping the database organized but, again, irrelevant to the end-user). And I can put routes under the "admin" group and not if inappropriate. That often means busting up a resource groups (create/store/update/destroy under admin, the rest not.

Again, there doesn't appear to be a way to specify all of this in CodeGenerator, and it may be that it just isn't feasible. Nonetheless, I'd definitely put it on a wish-list for feature enhancements.

CodeGenerator should be able to put the routes in the appropriate location («ProjectRoot»\Modules\«ModuleName»\Routes\web.php) instead of willynilly appending the routes to «ProjectRoot»\routes\web.php. I can't imagine what a gargantuan, monstrous, horrendous mess folks' web.php file must become without splitting up routes to where they are related. Even with code-folding in PhpStorm, that would be totally unmanageable.

mwidart/laravel-modules does an outstanding job of merging routes at runtime, as do most module packages. That's also a reason for structuring routes as nested route groups. A route like admin/common is not going to get merged with the admin group creating yet anothr mess.

This next is actually on my wish list for Laravel, as opposed to CodeGenerator, although it is doable via using a ->where regex in the route. Research repeatedly demonstrates that underscores introduce a hight degree of likelihood for typos in URLs (and practically everywhere else). In non-code-oriented users, they also create a high degree of frustration. I prefer that, at least, the model-specific portion of the route be dashed like this:

https://domain.com/admin/network/network-device-types/index.php

for the NetworkDeviceType model within the Network module. Most other frameworks either do this automatically or offer a dashed-route parameter.

Another thing that I haven't seen in CodeGenerator is support fir JSON (MySQL) fields which Laravel does support as "Associative Arrays" (Objects). In the Laravel blogosphere, these have become a hot item. I still haven't decided if my use cases warrant them, but they do have the potential for reducing the number of table columns for infrequently used cases (three lines in addresses, name-suffixes for credentials, other suffixes like Jr., Sr., III). I really hope that these are on your radar with a soon-to-come version update.

Steps/Commands To Reproduce:

  1. I produced a CodeGenerator_custom.php file; see the .txt file below. (Github won't permit attaching a file with a .php extension.)
  2. I executed the following command in a bash terminal.
    php artisan create:resources NetworkDevice --routes-prefix "admin/common" --models-per-page=15 --with-form-request --with-auth --table-name "network_devices" --table-exists --with-soft-delete --with-migration --force
  3. CodeGenerator put the views files in «ProjectRoot»\resources\views\admin\network. Unfortunately, the path should have been «ProjectRoot»\resources\views\admin\network\network-devices (or whatever variation on network-devices is wanted) if these files cannot be put under the Module. In any case, the issue with the structure still remains. NetworkDevice is just one of many models — along with controllers, views, etc. — that form the the Module Network.
  4. CodeGenerator created the following route (portions truncated for brevity):
    Route::group(
        [
            'prefix' => 'admin/network',
        ], function () {
            Route::get('/', 'NetworkDeviceController@index')
                ->name('admin.network.network_device.index');
    });

    This route will work actually quite well, although I'd prefer a dashed route (i.e., admin.network.network-device.index). If I could have more control over the generated route, what I would create manually would be more like admin.network.device.index in order to avoid repeating "network." I use the "network_" in the table name to better manage tables. The route clarifies that on its own.

In the End What's Needed

That's a very long way around describing what I'm trying to do. Here's the problem what I'm having: It's fairly easy to make changes to your package that have to do with templates. Being new to Laravel, it took me a while to get use to making template alterations, but I think I've gotten the hang of those. Where I'm having problems seem to be in other areas:

MikeAlhayek commented 5 years ago

Agreed! When time permits, I'll be sure to add support for module-based development.

elemes1 commented 5 years ago

@LarryTX this was my first thought when I saw this package, it would be an interesting feature to have +1