I have a base controller with this for example:
abstract class BaseController extends Controller
{
public function __construct(ModelRepositoryInterface $modelRepo)
{
$this->modelRepo = $modelRepo;
}
/**
* Create a new resource
*
* @authenticated
* @return \Illuminate\Http\JsonResponse
*/
protected function store(Request $request) {
$this->validateRequest($request);
$result = $this->modelRepo->store($request->all());
return response()->json($result);
}
}
And a child a controller that inherits from it that looks like this:
/**
* @group Node Management
*
* APIs for managing nodes
*/
class NodeController extends BaseController
{
public function __construct(NodeRepository $nodeRepo) {
parent::__construct($nodeRepo);
}
}
Is it possible to have something like this:
/**
* @group Node Management
*
* APIs for managing nodes
*/
class NodeController extends BaseController
{
public function __construct(NodeRepository $nodeRepo) {
parent::__construct($nodeRepo);
}
/**
*
* @method store
* @bodyParam name string required Name of the node.
*/
}
without the actual method in the controller since it's inherited?
No, that isn't doable, as docblocks have to be attached to specific elements, such as classes or functions. How do you define your routes, though? If you have a route pointing to [NodeController::class, 'store'], I believe that the package will try to fetch the store method of that class via reflection. That should return the inherited method and (I think) its docblock too.
Yes it does get the generic message I wrote in the Base Controller for all other child Controllers, however I want to specify specific bodyParams for each controller
Ah. That isn't doable via docblocks, as they must be attached to methods to be recognised as docblocks. You can either implement the method (just call parent::store()) or edit the documentation manually after generating.
@chinloyal @shalvah
Extending the strategy is your friend.
Here's how I manage my similar use-case..
use App\Http\Controllers\Controller;
abstract class BaseController extends Controller
{
protected $model;
/**
* Select All {model|str_plural}
*/
public function all()
{
return $this->model->get();
}
}
/**
* @group Addresses
*
* @authenticated
*/
class CountriesController extends BaseController
{
public function __construct(Country $model)
{
$this->model = $model;
}
}
I managed to to inspect the construct parameters and interpolate them on the documentation of the base controller. So for the CountriesController it will generate it as Select All Countries.
You can chain more function like {model|str_plural|snake_case}.
Just extend to the package's GetFromDocBlocks:
use Illuminate\Routing\Route;
use Mpociot\ApiDoc\Extracting\RouteDocBlocker;
use Mpociot\ApiDoc\Extracting\Strategies\Metadata\GetFromDocBlocks;
use ReflectionClass;
use ReflectionMethod;
class MyGetFromDocBlocks extends GetFromDocBlocks
{
public function __invoke(Route $route, ReflectionClass $controller, ReflectionMethod $method, array $routeRules, array $context = [])
{
$docBlocks = RouteDocBlocker::getDocBlocksFromRoute($route);
/** @var DocBlock $methodDocBlock */
$methodDocBlock = $docBlocks['method'];
$classDocBlock = $docBlocks['class'];
list($routeGroupName, $routeGroupDescription, $routeTitle) = $this->getRouteGroupDescriptionAndTitle($methodDocBlock, $classDocBlock);
$routeTitle = $this->getTitle($controller, $routeTitle ?: $methodDocBlock->getShortDescription());
return [
'groupName' => $routeGroupName,
'groupDescription' => $routeGroupDescription,
'title' => $routeTitle ?: $methodDocBlock->getShortDescription(),
'description' => $methodDocBlock->getLongDescription()->getContents(),
'authenticated' => $this->getAuthStatusFromDocBlock($classDocBlock->getTags())?:$this->getAuthStatusFromDocBlock($methodDocBlock->getTags()),
];
}
private function getTitle($controller, $routeTitle)
{
// if no curly brace return the default routeTitle
if (!preg_match_all('/{(.*?)}/', $routeTitle, $matches)) {
return $routeTitle;
}
$parameterNames = $matches[1];
foreach ($parameterNames as $parameterSignature) {
$modifiers = explode('|', $parameterSignature);
$parameterName = array_shift($modifiers);
$reflectParam = array_first(
$controller->getMethod('__construct')->getParameters(),
function ($reflectParam) use ($parameterName) {
return $reflectParam->getName() === $parameterName;
}
);
if (is_null($reflectParam)) {
continue;
}
$typeHintClass = last(explode('\\', $reflectParam->getType()->getName()));
foreach ($modifiers as $modifier) {
$typeHintClass = call_user_func_array($modifier, [$typeHintClass]);
}
$routeTitle = str_replace('{'.$parameterSignature.'}', $typeHintClass, $routeTitle);
}
return $routeTitle;
}
}
PS: I suggest we create a list of useful plugins from the community and we can put in the readme or documentation.
Wow, that's a really nice work! Sure, we can list these plugins. Please send a link to the GitHub repo.
@shalvah Thanks for the appreciation!
sorry but what do you mean _send a link to the GitHub repo_
what comes to my mind is send a PR for listing the plugins?
Plugins are installed as Composer packages, so they should probably have their own GitHub repo. So, I was asking to send a link to the repo links of any plugins you know of.
@ajcastro I've added your strategy to the new community wiki at https://github.com/knuckleswtf/scribe/wiki/Helpful-strategies-(snippets). Feel free to update it or add more.馃憤
Most helpful comment
@ajcastro I've added your strategy to the new community wiki at https://github.com/knuckleswtf/scribe/wiki/Helpful-strategies-(snippets). Feel free to update it or add more.馃憤