`php artisan create:api-scaffold --with-api-resource ...` creates invalid resource namespaces
Environment:
- Laravel-Code-Generator Version: 2.4
- Laravel Version: ^6.0
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."
}
}
}
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));
}
/**
@Orteko @renepardon this should be fixed in v3.0 which should be released soon