cslant/laravel-generator-advanced

View on GitHub
src/Http/Controllers/Detect/DetectController.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php

namespace CSlant\LaraGenAdv\Http\Controllers\Detect;

use Illuminate\Routing\Controller;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use ReflectionMethod;

class DetectController extends Controller
{
    /**
     * Get the type of all classes in the app folder.
     *
     * @return array[]
     */
    public function detect(): array
    {
        $recursiveDirectoryIterator = new RecursiveDirectoryIterator(app_path());
        $files = new RecursiveIteratorIterator($recursiveDirectoryIterator);
        $type = [];

        foreach ($files as $file) {
            if (!$file->isFile() || $file->getExtension() !== 'php') {
                continue;
            }

            $class = $this->getClassFromFile($file);
            if ($class !== null) {
                $type[] = $this->getClassType($class);
            }
        }

        return $type;
    }

    /**
     * @param $file
     * @return ReflectionClass|null
     */
    public function getClassFromFile($file): ?ReflectionClass
    {
        $content = file_get_contents($file);
        $matches = [];

        // Match namespace and class name
        preg_match('/namespace\s+(.*?);.*?class\s+(\w+)/s', $content, $matches);
        if (!isset($matches[1]) || !isset($matches[2])) {
            return null;
        }

        $namespace = $matches[1];
        $class = $namespace.'\\'.$matches[2];

        return class_exists($class) ? new ReflectionClass($class) : null;
    }

    /**
     * Get the type of the given class.
     *
     * @param  ReflectionClass  $class
     * @return string
     */
    protected function getClassType(ReflectionClass $class): string
    {
        $type = 'other';

        switch (true) {
            case $this->isRepositoryClass($class):
                $type = 'repository';

                break;
            case $this->isServiceClass($class):
                $type = 'service';

                break;
            case $this->isControllerClass($class):
                $type = 'controller';

                break;
            case $this->isActionClass($class):
                $type = 'action';

                break;
        }

        return $type;
    }

    /**
     * Check if the class is a repository class
     * A repository class must have a name ending with "Repository" or "EloquentRepository"
     * and implement the CRUD methods
     * and have a dependency on a model.
     *
     * @param  ReflectionClass  $class
     * @return bool
     */
    public function isRepositoryClass(ReflectionClass $class): bool
    {
        return $this->checkClassType($class, 'repository');
    }

    /**
     * Check if the class is a class of the given type
     * A class of the given type must have a name ending with the given type or "Eloquent" + the given type.
     *
     * @param  ReflectionClass  $class
     * @param $type
     * @return bool
     */
    protected function checkClassType(ReflectionClass $class, $type): bool
    {
        $type = ucfirst($type);

        return preg_match('/'.$type.'$/', $class->getName()) === 1
            || preg_match('/Eloquent'.$type.'$/', $class->getName()) === 1
            && $this->implementsCrudMethods($class)
            && $this->dependsOnModels($class);
    }

    /**
     * Check if the class implements the CRUD methods.
     *
     * @param  ReflectionClass  $class
     * @return bool
     */
    protected function implementsCrudMethods(ReflectionClass $class): bool
    {
        $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
        $crudMethods = [
            'create',
            'read',
            'update',
            'delete',
        ];

        foreach ($methods as $method) {
            if (in_array($method->name, $crudMethods)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  ReflectionClass  $class
     * @return bool
     */
    private function dependsOnModels(ReflectionClass $class): bool
    {
        $dependencies = $class->getConstructor()->getParameters();
        foreach ($dependencies as $dependency) {
            if (preg_match('/Model$/', $dependency->getClass()->getName()) === 1) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if the class is a service class
     * A service class must have a name ending with "Service" or "EloquentService".
     *
     * @param  ReflectionClass  $class
     * @return bool
     */
    public function isServiceClass(ReflectionClass $class): bool
    {
        return $this->checkClassType($class, 'service');
    }

    /**
     * Check if the class is a controller class
     * A controller class must have a name ending with "Controller" or "EloquentController"
     * and implement the CRUD methods
     * and have a dependency on a model.
     *
     * @param  ReflectionClass  $class
     * @return bool
     */
    public function isControllerClass(ReflectionClass $class): bool
    {
        return $this->checkClassType($class, 'controller');
    }

    /**
     * Check if the class is an action class
     * An action class must have a name ending with "Action" or "EloquentAction".
     *
     * @param  ReflectionClass  $class
     * @return bool
     */
    public function isActionClass(ReflectionClass $class): bool
    {
        return $this->checkClassType($class, 'action');
    }
}