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
738 stars 158 forks source link

`php artisan create:api-scaffold --with-api-resource ...` creates invalid resource namespaces #141

Closed renepardon closed 9 months ago

renepardon commented 4 years ago

Environment:

Description:

Generated resources are invalid. The namespaces are not build correctly

Steps/Commands To Reproduce:

cd /tmp && \
composer create-project laravel/laravel=6.2 test && \
cd test && \
composer require --dev crestapps/laravel-code-generator && \
git init && git add . && git commit -m "foobar" && \
php artisan create:api-scaffold \
    --with-api-resource \
    --with-form-request \
    --with-auth \
    --with-soft-delete \
    --with-migration \
    --translation-for=de,en \
    --language-filename=foo_bar \
    --table-name=foo_bars \
    --fields=id,parent_id,name,route,icon_class,order \
    FooBar

After this do a git status and you will receive something like this:

$ git status
Auf Branch master
Änderungen, die nicht zum Commit vorgemerkt sind:
  (benutzen Sie "git add <Datei>...", um die Änderungen zum Commit vorzumerken)
  (benutzen Sie "git checkout -- <Datei>...", um die Änderungen im Arbeitsverzeichnis zu verwerfen)

        geändert:       routes/api.php

Unversionierte Dateien:
  (benutzen Sie "git add <Datei>...", um die Änderungen zum Commit vorzumerken)

        app/Http/Controllers/Api/
        app/Http/Requests/
        "app/Http\\Resources\\Collections\\\\FooBarsCollection.php"
        "app/Http\\Resources\\\\/"
        app/Models/
        database/migrations/2019_12_16_124335_create_foo_bars_table.php
        resources/lang/de/
        resources/lang/en/foo_bar.php
        resources/laravel-code-generator/

Content Of The Resource-File:

$ cat resources/laravel-code-generator/sources/foo_bars.json 
{
    "fields": [
        {
            "name": "id",
            "labels": {
                "de": "Id",
                "en": "Id"
            },
            "html-type": "text",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "",
            "is-on-index": false,
            "is-on-show": false,
            "is-on-form": false,
            "data-type": "int",
            "data-type-params": [],
            "data-value": null,
            "is-index": false,
            "is-unique": false,
            "is-primary": true,
            "comment": null,
            "is-nullable": false,
            "is-header": false,
            "is-unsigned": true,
            "is-auto-increment": true,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Enter id here...",
                "en": "Enter id here..."
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": null,
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "id",
            "is-api-visible": true,
            "api-description": {
                "de": "The id of the model.",
                "en": "The id of the model."
            }
        },
        {
            "name": "parent_id",
            "labels": {
                "de": "Parent",
                "en": "Parent"
            },
            "html-type": "select",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "",
            "is-on-index": true,
            "is-on-show": true,
            "is-on-form": true,
            "data-type": "int",
            "data-type-params": [],
            "data-value": null,
            "is-index": true,
            "is-unique": false,
            "is-primary": false,
            "comment": null,
            "is-nullable": true,
            "is-header": false,
            "is-unsigned": true,
            "is-auto-increment": false,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Select parent",
                "en": "Select parent"
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": {
                "name": "parent",
                "type": "belongsTo",
                "params": [
                    "App\\Models\\Parent",
                    "parent_id"
                ],
                "field": "id"
            },
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "parent_id",
            "is-api-visible": true,
            "api-description": {
                "de": "The parent of the model.",
                "en": "The parent of the model."
            }
        },
        {
            "name": "name",
            "labels": {
                "de": "Name",
                "en": "Name"
            },
            "html-type": "text",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "string|min:1|max:255",
            "is-on-index": true,
            "is-on-show": true,
            "is-on-form": true,
            "data-type": "string",
            "data-type-params": [
                255
            ],
            "data-value": null,
            "is-index": false,
            "is-unique": false,
            "is-primary": false,
            "comment": null,
            "is-nullable": true,
            "is-header": true,
            "is-unsigned": false,
            "is-auto-increment": false,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Enter name here...",
                "en": "Enter name here..."
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": null,
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "name",
            "is-api-visible": true,
            "api-description": {
                "de": "The name of the model.",
                "en": "The name of the model."
            }
        },
        {
            "name": "route",
            "labels": {
                "de": "Route",
                "en": "Route"
            },
            "html-type": "text",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "string|min:1",
            "is-on-index": true,
            "is-on-show": true,
            "is-on-form": true,
            "data-type": "string",
            "data-type-params": [],
            "data-value": null,
            "is-index": false,
            "is-unique": false,
            "is-primary": false,
            "comment": null,
            "is-nullable": true,
            "is-header": false,
            "is-unsigned": false,
            "is-auto-increment": false,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Enter route here...",
                "en": "Enter route here..."
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": null,
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "route",
            "is-api-visible": true,
            "api-description": {
                "de": "The route of the model.",
                "en": "The route of the model."
            }
        },
        {
            "name": "icon_class",
            "labels": {
                "de": "Icon Class",
                "en": "Icon Class"
            },
            "html-type": "text",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "string|min:1",
            "is-on-index": true,
            "is-on-show": true,
            "is-on-form": true,
            "data-type": "string",
            "data-type-params": [],
            "data-value": null,
            "is-index": false,
            "is-unique": false,
            "is-primary": false,
            "comment": null,
            "is-nullable": true,
            "is-header": false,
            "is-unsigned": false,
            "is-auto-increment": false,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Enter icon class here...",
                "en": "Enter icon class here..."
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": null,
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "icon_class",
            "is-api-visible": true,
            "api-description": {
                "de": "The icon class of the model.",
                "en": "The icon class of the model."
            }
        },
        {
            "name": "order",
            "labels": {
                "de": "Order",
                "en": "Order"
            },
            "html-type": "text",
            "css-class": "",
            "options": {},
            "html-value": null,
            "validation": "string|min:1",
            "is-on-index": true,
            "is-on-show": true,
            "is-on-form": true,
            "data-type": "string",
            "data-type-params": [],
            "data-value": null,
            "is-index": false,
            "is-unique": false,
            "is-primary": false,
            "comment": null,
            "is-nullable": true,
            "is-header": false,
            "is-unsigned": false,
            "is-auto-increment": false,
            "is-inline-options": false,
            "is-date": false,
            "date-format": "",
            "cast-as": "",
            "placeholder": {
                "de": "Enter order here...",
                "en": "Enter order here..."
            },
            "delimiter": "; ",
            "range": [],
            "foreign-relation": null,
            "foreign-constraint": null,
            "on-store": null,
            "on-update": null,
            "api-key": "order",
            "is-api-visible": true,
            "api-description": {
                "de": "The order of the model.",
                "en": "The order of the model."
            }
        }
    ],
    "relations": [],
    "indexes": [],
    "auto-manage-created-and-updated-at": true,
    "table-name": null,
    "protection": {
        "is-model-protected": false,
        "is-controller-protected": false,
        "is-api-resource-protected": false,
        "is-api-resource-collection-protected": false,
        "is-api-documentation-protected": false,
        "is-api-documentation-controller-protected": false,
        "is-form-request-protected": false,
        "is-languages-protected": false,
        "is-form-view-protected": false,
        "is-index-view-protected": false,
        "is-create-view-protected": false,
        "is-edit-view-protected": false,
        "is-show-view-protected": false
    },
    "api-documentation": {
        "access_token_with_bearer": {
            "de": "The access token prefixed with the \"Bearer \" key word.",
            "en": "The access token prefixed with the \"Bearer \" key word."
        },
        "index_route_description": {
            "de": "Retrieve existing foo bars.",
            "en": "Retrieve existing foo bars."
        },
        "index_route_response_description": {
            "de": "The API's response will be JSON based data. The JSON object will be structured as follow",
            "en": "The API's response will be JSON based data. The JSON object will be structured as follow"
        },
        "the_key_is_the_model_property_and_the_value_is_the_model_value": {
            "de": "The array's key is the foo bar property name where the value is the assigned value to the retrieved foo bar.",
            "en": "The array's key is the foo bar property name where the value is the assigned value to the retrieved foo bar."
        },
        "link_to_retrieve_first_page": {
            "de": "Link to retrieve first page.",
            "en": "Link to retrieve first page."
        },
        "link_to_retrieve_last_page": {
            "de": "Link to retrieve last page.",
            "en": "Link to retrieve last page."
        },
        "link_to_retrieve_previous_page": {
            "de": "Link to retrieve previous page.",
            "en": "Link to retrieve previous page."
        },
        "link_to_retrieve_next_page": {
            "de": "Link to retrieve next page.",
            "en": "Link to retrieve next page."
        },
        "the_number_of_current_page": {
            "de": "The number of current page.",
            "en": "The number of current page."
        },
        "the_index_of_the_first_retrieved_item": {
            "de": "The index of first retrieved foo bar.",
            "en": "The index of first retrieved foo bar."
        },
        "the_number_of_the_last_page": {
            "de": "The number of the last page.",
            "en": "The number of the last page."
        },
        "the_base_link_to_the_resource": {
            "de": "The base link to the api resource.",
            "en": "The base link to the api resource."
        },
        "the_number_of_models_per_page": {
            "de": "The number of foo bars per page.",
            "en": "The number of foo bars per page."
        },
        "the_index_of_the_last_retrieved_item": {
            "de": "The index of last retrieved foo bar.",
            "en": "The index of last retrieved foo bar."
        },
        "the_total_of_available_pages": {
            "de": "The total of the available pages.",
            "en": "The total of the available pages."
        },
        "store_route_description": {
            "de": "Create new foo bar.",
            "en": "Create new foo bar."
        },
        "store_route_response_description": {
            "de": "The API's response will be JSON based data. The JSON object will be structured as follow",
            "en": "The API's response will be JSON based data. The JSON object will be structured as follow"
        },
        "update_route_description": {
            "de": "Update existsing foo bar.",
            "en": "Update existsing foo bar."
        },
        "update_route_response_description": {
            "de": "The API's response will be JSON based data. The JSON object will be structured as follow",
            "en": "The API's response will be JSON based data. The JSON object will be structured as follow"
        },
        "show_route_description": {
            "de": "Retrieve existsing foo bar.",
            "en": "Retrieve existsing foo bar."
        },
        "show_route_response_description": {
            "de": "The API's response will be JSON based data. The JSON object will be structured as follow",
            "en": "The API's response will be JSON based data. The JSON object will be structured as follow"
        },
        "the_id_of_model_to_retrieve": {
            "de": "The unique id of the foo bar to retrieve",
            "en": "The unique id of the foo bar to retrieve"
        },
        "destroy_route_description": {
            "de": "Delete existsing foo bar.",
            "en": "Delete existsing foo bar."
        },
        "destroy_route_response_description": {
            "de": "The API's response will be JSON based data. The JSON object will be structured as follow",
            "en": "The API's response will be JSON based data. The JSON object will be structured as follow"
        },
        "the_id_of_model_to_delete": {
            "de": "The id of the foo bar to delete.",
            "en": "The id of the foo bar to delete."
        },
        "general_description": {
            "de": "Allows you to list, create, edit, show and delete foo bars.",
            "en": "Allows you to list, create, edit, show and delete foo bars."
        },
        "indicate_whether_the_request_was_successful_or_not": {
            "de": "Indicate whether the request was successful or not.",
            "en": "Indicate whether the request was successful or not."
        },
        "the_id_of_the_model": {
            "de": "The id of the foo bar.",
            "en": "The id of the foo bar."
        },
        "this_parameter_must_be_present_in_the_request": {
            "de": "This parameter must be present in the request.",
            "en": "This parameter must be present in the request."
        },
        "the_request_failed_validation": {
            "de": "The request failed validation.",
            "en": "The request failed validation."
        },
        "list_of_the_invalid_errors": {
            "de": "List of the invalid errors.",
            "en": "List of the invalid errors."
        },
        "the_requested_model_does_not_exists": {
            "de": "The requested foo bar does not exists.",
            "en": "The requested foo bar does not exists."
        },
        "the_user_does_not_have_permission_to_access_the_requested_resource": {
            "de": "User does not have permission to access the requested resource.",
            "en": "User does not have permission to access the requested resource."
        }
    }
}
Orteko commented 4 years ago

I'm not sure if this project is still being actively developed however for future reference this appears to be a bug in src/Traits/ApiResourceTrait.php where it doesn't honour the system path separator.

Something similar to the following is likely required - although this fixes the path separator the actual directory itself (being based on the namespace command) doubles up as app/App which doesn't seem correct.

The following "fixes" the issue but I suspect there is almost certainly another helper method i'm unaware of that should be used rather than these functions using getAppNamespace:

diff --git a/src/Traits/ApiResourceTrait.php b/src/Traits/ApiResourceTrait.php
index 286af78..86d6cc4 100644
--- a/src/Traits/ApiResourceTrait.php
+++ b/src/Traits/ApiResourceTrait.php
@@ -136,7 +136,9 @@ trait ApiResourceTrait

         $path = Helpers::getAppNamespace(Config::getApiResourcePath(), $path, $this->option('api-version'));

-        return Helpers::getPathWithSlash($path);
+        $path = preg_replace('/^(App\\\)/', '', $path);
+
+        return Helpers::getPathWithSlash(Helpers::fixPathSeparator($path));
     }

     /**
@@ -152,7 +154,11 @@ trait ApiResourceTrait
             $path = Helpers::getPathWithSlash($path);
         }

-        return Helpers::getAppNamespace(Config::getApiResourceCollectionPath(), $path, $this->option('api-version'));                                                                                                                       
+        $path = Helpers::getAppNamespace(Config::getApiResourceCollectionPath(), $path, $this->option('api-version'));                                                                                                                      
+                                                                                                                                                                                                                                            
+        $path = preg_replace('/^(App\\\)/', '', $path);                                                                                                                                                                                     
+                                                                                                                                                                                                                                            
+        return Helpers::getPathWithSlash(Helpers::fixPathSeparator($path));                                                                                                                                                                 
     }                                                                                                                                                                                                                                       

     /**
MikeAlhayek commented 9 months ago

@Orteko @renepardon this should be fixed in v3.0 which should be released soon