OK
Here's what I investigated
when calling command like this
php artisan api:generate --router="dingo" --routePrefix="v1" --actAsUserId=1 --header="Authorization : Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6Ly9pbXVzaWZ5LmxhcmEubG9jL2FwaS9hdXRoZW50aWNhdGUiLCJpYXQiOjE0OTczNzEzNDYsImV4cCI6MTQ5NzM3NDk0NiwibmJmIjoxNDk3MzcxMzQ2LCJqdGkiOiJKeWlITWhvODZLcHVOZE1kIn0.mTNK1HO8b39QOrRBWlKfP5xxhLA2s_7RGgNiO4vD1so"
I got 'Null' on every response example in the docs...
I went to /Mpociot/ApiDoc/Generators/DingoGenerator.php method processRoute line 24 and dumped the exception here like this
try {
$response = $this->getRouteResponse($route, $bindings, $headers);
} catch (Exception $e) {
dump($e);
}
And gues what?!
I get Exception on EVERY call !!!
Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException {#2074
-statusCode: 401
-headers: array:1 [
"WWW-Authenticate" => null
]
#message: "Failed to authenticate because of bad credentials or an invalid authorization header."
That's why there's no response example on any route at all
If someone interested I attached stack_trace.txt here
Please someone tell how to fix or bypass this
And if I remove the middleware api doc response examples become something like this
{
"exception": null,
"headers": {},
"original": {
"id": 1,
"email": "[email protected]",
"name": "Barney Ledner",
"birthday": "1980-11-05 00:00:00",
"country": "Switzerland",
"city": "Lake Tryciaport",
"website": "hilpert.biz",
"created_at": "2017-06-13 13:06:22",
"updated_at": "2017-06-13 13:06:22"
}
}
Instead of being transformed to
{
"data": {
"name": "Norberto Schmitt",
"email": "[email protected]",
"created": "2017-06-06"
}
}
For example, I have a controller method
````
/**
* List
*
* @transformercollection \App\Transformers\UserTransformer
*
* @response {
* data: [],
* }
* @return \Dingo\Api\Http\Response
*/
public function index()
{
$users = User::paginate();
return $this->response->paginator($users, new UserTransformer);
}
```
api:generatecommand ignores all phpDoc notaions@transformercollection,@transformer,@response`....
what I get is this

it just uses some 'default' response structure
I think I responded to you earlier about the JWT header thing.... use the latest development build instead of version 2.0 to get access to this fix
As of writing, the Dingo generator doesn't recognise the @response doc block... I've forked the project and implemented it (but only for 1.7 since I'm stuck with Laravel 5.2 at the moment...). You could try and checkout my DingoGenerator.php file (but note that you will need to manually implement the changes to the file in this commit). No guarantees it will work for you as I haven't tested it thoroughly yet.
and what about transformers?
what about @transformer doc block?
any thoughts?
Here's my composer.json

and here's my /vendor/mpociot/laravel-apidoc-generator/src/Mpociot/ApiDoc/Generators/DingoGenerator.php

I managed to get responses using --header= option from 'auth' middleware protected routes (with dev-master version only)
But they are raw... and I need them transformed
Neither option (Transformer class / @transformer doc block) works....
And second thing is that some routes need special conditions to return right values (like auth routes)
and in that cases @response doc block comes handy...
but it doesn't work
So in conclusion...
If I use this package, I DON"T receive:
All you can get is raw (untransformed / unfomatted / not customizable) GET responses
So i'm not getting HALF of functionality it should be providing...
It appears barely useful in real projects...
Sad but true
Ah yeah, the DingoGenerator seems to get little love compared to it's LaravelGenerator brother. I've been hacking away at the code recently so I've learned a lot about it.
All the doc block handling (@response and @transformer) is not included for Dingo routes at the moment. My personal fork for 1.7.0 works for @response (no testing of @transformer yet) but I've yet to port it over to the master branch and do a PR.
The code does not create POST/PUT/PATCH/DELETE method responses because it doesn't to deal with the hassle, which is why it uses @response. Unfortunately it doesn't work for Dingo atm.
As a quick hack (and also a test to see if my changes works for the master branch).... could you make the following code changes (back-up first, or at least know how to revert the changes):
Replace function processRoute() in file /vendor/mpociot/laravel-apidoc-generator/src/Mpociot/ApiDoc/Generators/DingoGenerator.php with:
public function processRoute($route, $bindings = [], $headers = [], $withResponse = true)
{
$content = '';
$routeAction = $route->getAction();
$routeGroup = $this->getRouteGroup($routeAction['uses']);
$routeDescription = $this->getRouteDescription($routeAction['uses']);
$showresponse = null;
if ($withResponse) {
$response = null;
$docblockResponse = $this->getDocblockResponse($routeDescription['tags']);
if ($docblockResponse) {
// we have a response from the docblock ( @response )
$response = json_decode($docblockResponse->getContent());
$showresponse = true;
}
if (! $response) {
$transformerResponse = $this->getTransformerResponse($routeDescription['tags']);
if ($transformerResponse) {
// we have a transformer response from the docblock ( @transformer || @transformercollection )
$response = json_decode($transformerResponse->getContent());
$showresponse = true;
}
}
if (! $response) {
try {
$response = $this->getRouteResponse($route, $bindings, $headers);
if (is_object($response)) {
$response = json_encode(json_decode($response->getContent(), JSON_PRETTY_PRINT));
}
} catch (Exception $e) {
}
}
$content = $response;
}
return $this->getParameters([
'id' => md5($route->uri().':'.implode($route->getMethods())),
'resource' => $routeGroup,
'title' => $routeDescription['short'],
'description' => $routeDescription['long'],
'methods' => $route->getMethods(),
'uri' => $route->uri(),
'parameters' => [],
'response' => $content,
'showresponse' => $showresponse,
], $routeAction, $bindings);
}
Add this completely new function anywhere in /vendor/mpociot/laravel-apidoc-generator/src/Mpociot/ApiDoc/Generators/DingoGenerator.php (THIS IS THE @transformer PARSER FUNCTION!)
/**
* Get a response from the transformer tags.
*
* @param array $tags
*
* @return mixed
*/
protected function getTransformerResponse($tags)
{
try {
$transFormerTags = array_filter($tags, function ($tag) {
if (! ($tag instanceof Tag)) {
return false;
}
return \in_array(\strtolower($tag->getName()), ['transformer', 'transformercollection']);
});
if (empty($transFormerTags)) {
// we didn't have any of the tags so goodbye
return false;
}
$modelTag = array_first(array_filter($tags, function ($tag) {
if (! ($tag instanceof Tag)) {
return false;
}
return \in_array(\strtolower($tag->getName()), ['transformermodel']);
}));
$tag = \array_first($transFormerTags);
$transformer = $tag->getContent();
if (! \class_exists($transformer)) {
// if we can't find the transformer we can't generate a response
return;
}
$demoData = [];
$reflection = new ReflectionClass($transformer);
$method = $reflection->getMethod('transform');
$parameter = \array_first($method->getParameters());
$type = null;
if ($modelTag) {
$type = $modelTag->getContent();
}
if (version_compare(PHP_VERSION, '7.0.0') >= 0 && \is_null($type)) {
// we can only get the type with reflection for PHP 7
if ($parameter->hasType() &&
! $parameter->getType()->isBuiltin() &&
\class_exists((string) $parameter->getType())) {
//we have a type
$type = (string) $parameter->getType();
}
}
if ($type) {
// we have a class so we try to create an instance
$demoData = new $type;
try {
// try a factory
$demoData = \factory($type)->make();
} catch (\Exception $e) {
if ($demoData instanceof \Illuminate\Database\Eloquent\Model) {
// we can't use a factory but can try to get one from the database
try {
// check if we can find one
$newDemoData = $type::first();
if ($newDemoData) {
$demoData = $newDemoData;
}
} catch (\Exception $e) {
// do nothing
}
}
}
}
$fractal = new Manager();
$resource = [];
if ($tag->getName() == 'transformer') {
// just one
$resource = new Item($demoData, new $transformer);
}
if ($tag->getName() == 'transformercollection') {
// a collection
$resource = new Collection([$demoData, $demoData], new $transformer);
}
return \response($fractal->createData($resource)->toJson());
} catch (\Exception $e) {
// it isn't possible to parse the transformer
return;
}
}
NOTE: This might mess things up so make sure you know how to revert or reinstall!
BTW, does the API end-points output correctly elsewhere (i.e. in a RESTful client like Postman)?
Almost forgot... you need to include this at the top of the DingoGenerator file...
use League\Fractal\Manager;
use League\Fractal\Resource\Item;
use Mpociot\Reflection\DocBlock\Tag;
use League\Fractal\Resource\Collection;
Also, do a print out of the $headers variable in processRoutes() while sending the auth token.... this is to see if the token is correctly being added (I had trouble in 1.7.0 as the --header option mangled my token)

Hey mate, just so I can work these changes into my fork (and hopefully a PR) can you tell me your results?
will tell you results soon (~21-06-17)

Headers are fine
But I didn't manage to make it work...
Also u forgot use ReflectionClass; on the top
It might be my Transformer
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => $user->id,
'name' => ucwords($user->name),
'email' => $user->email,
'birthday' => $user->birthday->toDateString(),
'country' => ucwords($user->country),
'city' => ucwords($user->city),
'website' => $user->website,
'created' => $user->created_at->toDateString(),
];
}
}
Here's why

Your function somehow not fetching ID and timestamps of the model....
Any thoughts?
Thought it needs improving it gives expected results

Good job!
Also as an improvement
in case of multiple entities in the response
it would be nice to cut it up to 2 elems
My bad...
If you use @transformercollection it realy generates 2-piece response
But it cuts of the meta data if I use
````
public function index()
{
$tracks = Track::paginate();
return $this->response->paginator($tracks, new TrackTransformer);
}
````
It's not so crucial and i believe is a subject for further improvements
If it helps
If I dump data here
$demoData = $newDemoData;
}
} catch (\Exception $e) {
// do nothing
}
}
}
}
dump($demoData);
$fractal = new Manager();
$resource = [];
if ($tag->getName() == 'transformer') {
// just one
$resource = new Item($demoData, new $transformer);
}
in the getTransformerResponse function
I get this
App\Models\Track {#1838
#guarded: array:1 [
0 => "id"
]
#fillable: array:2 [
0 => "name"
1 => "url"
]
#hidden: []
#dates: []
#casts: []
#connection: null
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: false
+wasRecentlyCreated: false
#attributes: array:3 [
"name" => "Sint voluptatum est sed excepturi."
"url" => "tracks/test.mp3"
"user_id" => 10
]
#original: []
#dateFormat: null
#appends: []
#events: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#visible: []
}
I believe the reason is here
// try a factory
$demoData = \factory($type)->make();
make() method won't create real DB record so there's no ID and timestamps
I haven't really tested the @transformer code (I just straight up copy-pasted it from LaravelGenerator.php and my project's API is a bit ancient and doesn't use transformers yet) so this is good to know.
Looking at the getTransformerResponse() function it tries to use the factory method to generate demo data, but if that fails it will try to grab real data from a database. Maybe if you temporarily comment out your Faker factory declaration and run the generation code again it will use your database instead (if you really want to have 'id' and 'created_at' fields properly filled in).
The other option is to edit your Transformer so that it doesn't call toDateString() if the data is null, like:
'created' => (($user->created_at) ? $user->created_at->toDateString() : $user->created_at),
As for the missing 'meta' field... I'm not sure so I will have to look around.
If I comment this section
// try {
// // try a factory
// $demoData = \factory($type)->make();
// } catch (\Exception $e) {
It works!


We're half way down the way with Dingo Generator))
@response still not working
This is what i have

And this is what i get

@almightynassar any thought how to make @response work?
Can you turn the data entry into a string, like:
@response {
"data": [],
}
If that doesn't work, can you try without the @transformer \App\Transformers\TrackTransformer line in the doc block? Or maybe switch them around?
Also do a data dump of the $docblockResponse variable after it is set in processRoute().
@almightynassar doin it with fresh migration and seeding every time


But nothing changes....

dump of the $docblockResponse

Hmm well your dump shows that the doc block is being read... it is just not using it when it comes time to generating the output. The content and original fields hold the data we want.
$docblockResponse is supposed to be transferred to the $response variable (and then the $content variable is set with $response and converted into it's PRETTY_PRINT form). So even though $docblockResponse is set, it is getting overridden by one of the later if-else blocks. In fact, the Example Response section only shows if $showResponse is set to true... which should only happen if a value for $response is set, and it looks like $showResponse isn't being set at all.
Can you do a dump of $content and $showResponse before it get's returned?
Sure, but tomorrow
Ср, 21 черв. 2017 18:39 користувач Almighty Nassar notifications@github.com
пише:
Hmm well your dump shows that the doc block is being read... it is just
not using it when it comes time to generating the output. The content and
original fields hold the data we want.$docblockResponse is supposed to be transferred to the $response variable
(and then the $content variable is set with $response and converted into
it's PRETTY_PRINT form). So even though $docblockResponse is set, it is
getting overridden by one of the later if-else blocks. In fact, the Example
Response section only shows if $showResponse is set to true... which should
only happen if a value for $response is set, and it looks like
$showResponse isn't being set at all.Can you do a dump of $content and $showResponse before it get's returned?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/mpociot/laravel-apidoc-generator/issues/201#issuecomment-310119176,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AFBBDLXEsNmAjwTueb7biI4vrb38pMjrks5sGTkpgaJpZM4N41Jc
.

also I dumped ready $parsedRoutes and it has response

So it's up to Markdown generation... I keep investigating...
here I dumpred from writeMarkdown method of
/vendor/mpociot/laravel-apidoc-generator/src/Mpociot/ApiDoc/Commands/GenerateDocumentation.php
where it parsed routes

and it ALSO has that right response
then $parsedRouteOutput ALSO has it

I've found one issue
The response (somehow?) should be json parsed two times to be an array (not a string)
In vendor/mpociot/laravel-apidoc-generator/src/Mpociot/ApiDoc/Generators/DingoGenerator.php
processRoute method I did this
````
if ($withResponse) {
$response = null;
$docblockResponse = $this->getDocblockResponse($routeDescription['tags']);
if ($docblockResponse) {
// we have a response from the docblock ( @response )
$response = json_decode(json_decode($docblockResponse->getContent()));
$showresponse = true;
}
....
```
noticejson_decode(json_decode($docblockResponse->getContent()));`
after that the response parsed to array finaly
BUT it still not showing in the docs... :sob:
Here's full console dump of that parsed route
```
array:10 [
"id" => "74bbc2252045e93d6e5e7ad8920c058c"
"resource" => "Tracks"
"title" => """
Create\n
Show the form for creating a new resource.
"""
"description" => ""
"methods" => array:1 [
0 => "POST"
]
"uri" => "/api/tracks"
"parameters" => array:1 [
"name" => array:5 [
"required" => false
"type" => "string"
"default" => ""
"value" => "distinctio"
"description" => array:2 [
0 => "Minimum:2"
1 => "Maximum:255`"
]
]
]
"response" => {#1114
+"data": {#1113
+"id": 12
+"name": "New Track"
+"user_id": 5
+"url": "http://localhost/storage/tracks/q1mu1b7N6DHAF608zURcGnljcQ00ju6wi17QgDyd.bin"
+"created": "2017-06-20"
}
}
"showresponse" => true
"output" => """
\n
## Create\n
Show the form for creating a new resource.\n
\n
Example request:\n
\n
bash\n curl -X POST "http://localhost//api/tracks" \\n -H "Accept: application/json" \\n -d "name"="distinctio" \\n \n\n
\n
javascript\n var settings = {\n "async": true,\n "crossDomain": true,\n "url": "http://localhost//api/tracks",\n "method": "POST",\n "data": {\n "name": "distinctio"\n },\n "headers": {\n "accept": "application/json"\n }\n }\n \n $.ajax(settings).done(function (response) {\n console.log(response);\n });\n\n
\n
\n
### HTTP Request\n
POST /api/tracks\n
\n
#### Parameters\n
\n
Parameter | Type | Status | Description\n
--------- | ------- | ------- | ------- | -----------\n
name | string | optional | Minimum:2Maximum:255\n
\n
\n
"""
]
````
Just letting you know I'm still looking into this. Got caught up in some other projects.
It probably isn't related to your issue, but the DB::beginTransaction(); code I told you about in issue #204 for seems to error out on me. After a few route calls I start getting PDOExceptions and null values for the rest of my routes.
But this shouldn't be an issue for you since you are getting valid responses, just that the responses are not being added into the Blade template.
In the routes.blade.php partial, on around line 44 do a dump of $parsedRoute['response'] (something like <p>Route Response: {{$parsedRoute['response']}}</p> and see if it is still null there.
The other thing you need to output is $parsedRoute['showresponse']. If that is not set to true for POST|PUT|DELETE then the example response will not show up.
@almightynassar I don't know how to dump info from blade files. Do you know how?
@almightynassar I believe my changes to package tpl files won't take effect
The template files are stored in /vendor/mpociot/laravel-apidoc-generator/src/resources/views/partials/route.blade.php
You don't really need to dump the data, just display it
Sometimes the public/docs location doesn't get rebuilt, so you may have to delete the docs directory just so it correctly re-generates everything.
Well I tried... couple times, different ways... nothing...
Data seems to be in place but never printed in the template
And any changes I do with the blade files don't take effect
That's why I was asking
Пн, 26 черв. 2017 23:02 користувач Almighty Nassar notifications@github.com
пише:
The template files are stored in
/vendor/mpociot/laravel-apidoc-generator/src/resources/views/partials/route.blade.phpYou don't really need to dump the data, just display it
Sometimes the public/docs location doesn't get rebuilt, so you may have to
delete the docs directory just so it correctly re-generates everything.—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/mpociot/laravel-apidoc-generator/issues/201#issuecomment-311166009,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AFBBDAfBtS8vZ1sQer2N10crxG0HRffEks5sIA5CgaJpZM4N41Jc
.
It's an annoying thing about Documentarian.... it seems to cache the files somewhere and only use the updated blade templates when it feels like. I hated it so much so that I added some code into my fork to turn off the part where it tries to intelligently "compare and update" the documentation.
The parts you want to comment out are in the GenerateDocumentation.php. Basically you need to look out for and comment out the following (my fork is heavily modified so let me know if things don't match up):
Anywhere that has $compareMarkdown
file_put_contents($compareFile, $compareMarkdown);
The wholeif (file_exists($targetFile) && file_exists($compareFile)) { block
Delete any compare.md or similar file in public/docs and Documentarian _should_ recreate everything from the base template (at least it does it for me).
Ok, I've bundled all of my custom changes to forked version of 1.7.0 and merged them into my fork of the master branch. This should include pretty much everything we talked about in this thread. Most importantly these changes have worked for me but I have yet to test them in a Laravel 5.4 environment.
I've put this out so you can do a compare of your edits with what I have. If you want to directly use my code in your project (and you are using composer), just add the following before your "require" entries into your composer.json
"repositories": [
{
"type": "vcs",
"url": "https://github.com/almightynassar/laravel-apidoc-generator"
}
],
You should be able to keep your current "mpociot/laravel-apidoc-generator": "^2.0" or dev-master entry, but if it does not automatically use my branch just use "mpociot/laravel-apidoc-generator": "2.0.1"
@almightynassar sorry, was really busy
Experimented with templates but here what I found...
I published all necessary files while installing the package and among those were also blade templates for docs at resources/views/vendor/apidoc! :roll_eyes:
Than I commented out this IF section @if(in_array('GET',$parsedRoute['methods'])) and voila!
It works!

So theres no need to cut down all stuff related to $compareMarkdown ))
Thanks A LOT @almightynassar for your work!
hope @mpociot sees this thread and will accept your corrections
Sweet! I thought the publish feature only created the language files so I didn't bother with it (in hindsight I should have known better :smile: ). Glad it is finally working for you.
@c00p3r I can't find the directory _resources/views/vendor/apidoc_ you told on comment. Can you show me where it is?
@dinhquyf0
you need to do this

It's not said clearly in the docs
But it actualy publishes two folders...
Source:
$this->publishes([
__DIR__.'/../../resources/lang' => $this->resource_path('lang/vendor/apidoc'),
__DIR__.'/../../resources/views' => $this->resource_path('views/vendor/apidoc'),
]);
Most helpful comment
@dinhquyf0

you need to do this
It's not said clearly in the docs
But it actualy publishes two folders...
Source:
$this->publishes([ __DIR__.'/../../resources/lang' => $this->resource_path('lang/vendor/apidoc'), __DIR__.'/../../resources/views' => $this->resource_path('views/vendor/apidoc'), ]);