PHP code example of wappcode / gqlpdss

1. Go to this page and download the library: Download wappcode/gqlpdss library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

wappcode / gqlpdss example snippets



return [
    // Configuración específica del módulo
    'version' => '1.0.0',
    'description' => 'Módulo principal de la aplicación'
];



namespace AppModule;

use AppModule\Entities\User;
use DateTime;
use GPDCore\Contracts\AppContextInterface;
use GPDCore\Core\AbstractModule;
use GPDCore\Graphql\ResolverFactory;
use GPDCore\Graphql\ResolverPipelineFactory;

class AppModule extends AbstractModule
{
    /**
     * Configuración del módulo
     */
    public function getConfig(): array
    {
        return         'factories' => [],
            'aliases' => []
        ];
    }

    /**
     * Tipos GraphQL personalizados
     */
    public function getTypes(): array
    {
        return [];
    }

    /**
     * Middlewares HTTP del módulo
     */
    public function getMiddlewares(): array
    {
        return [];
    }

    /**
     * Rutas REST del módulo (opcional)
     */
    public function getRoutes(): array
    {
        return [];
    }

    /**
     * Resolvers GraphQL del módulo
     */
    public function getResolvers(): array
    {
        // Middleware de ejemplo
        $proxyEcho1 = fn($resolver) => fn($root, $args, $context, $info) => 
            'Proxy 1 ' . $resolver($root, $args, $context, $info);
        $proxyEcho2 = fn($resolver) => fn($root, $args, $context, $info) => 
            'Proxy 2 ' . $resolver($root, $args, $context, $info);
        $echoResolve = fn($root, $args, $context, $info) => $args['msg'];

        return [
            // Resolver simple
            'Query::showDate' => fn($root, $args, AppContextInterface $context, $info) => new DateTime(),
            'Query::echo' => $echoResolve,
            
            // Resolvers con middleware pipeline
            'Query::echoProxy' => ResolverPipelineFactory::createPipeline($echoResolve, [
                ResolverPipelineFactory::createWrapper($proxyEcho1),
            ]),
            'Query::echoProxies' => ResolverPipelineFactory::createPipeline($echoResolve, [
                ResolverPipelineFactory::createWrapper($proxyEcho2),
                ResolverPipelineFactory::createWrapper($proxyEcho1),
            ]),
            
            // Resolvers CRUD automáticos usando ResolverFactory
            'Query::getUsers' => ResolverFactory::forConnection(User::class),
            'Query::getUser' => ResolverFactory::forItem(User::class),
            'Mutation::createUser' => ResolverFactory::forCreate(User::class),
            'Mutation::updateUser' => ResolverFactory::forUpdate(User::class),
            'Mutation::deleteUser' => ResolverFactory::forDelete(User::class),
        ];
    }

    /**
     * Campos Query adicionales (opcional)
     */
    public function getQueryFields(): array
    {
        return [];
    }
}


return [
    // Configuración general de la aplicación
    'app' => [
        'name' => 'Mi API GraphQL',
        'version' => '1.0.0',
        'debug' => false
    ],
    
];


return [
    "AppModule\\Entities" => __DIR__ . "/../modules/AppModule/src/Entities",
];


return [
    "driver" => [
        'user'     => 'root',
        'password' => 'password',
        'dbname'   => 'mi_database',
        'driver'   => 'pdo_mysql',
        'host'     => '127.0.0.1',
        'charset'  => 'utf8mb4'
    ],
    "entities" => 



use AppModule\AppModule;
use GPDCore\Contracts\AppContextInterface;
use GPDCore\Core\AppConfig;
use GPDCore\Core\Application;
use GPDCore\Factory\EntityManagerFactory;
use GraphqlModule\GraphqlModule;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Laminas\ServiceManager\ServiceManager;

$masterConfig);

// Inicializar ServiceManager
$serviceManager = new ServiceManager();

// Crear EntityManager
$entityManagerOptions = file_exists($configFile) ? ;                      // Módulo principal

// Ejecutar aplicación y emitir respuesta
$response = $app->run($request);
$emitter = new SapiEmitter();
$emitter->emit($response);

// Aplicación en la raíz del dominio → baseHref no es necesario
// https://midominio.com/api  →  ruta interna: /api
$app = new Application($config, $entityManager, $environment);

// Aplicación en una subcarpeta → baseHref es obligatorio
// https://midominio.com/mi-app/api  →  ruta interna: /api
$app = new Application($config, $entityManager, $environment, '/mi-app/public');



use GPDCore\Factory\EntityManagerFactory;
use Doctrine\ORM\Tools\Console\ConsoleRunner;

neORMModule";
$entityManager = EntityManagerFactory::createInstance($options, $cacheDir, true);

return ConsoleRunner::createHelperSet($entityManager);



namespace AppModule\Entities;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use PDSSUtilities\AbstractEntityModelUlid;

#[ORM\Entity()]
#[ORM\Table(name: 'users')]
class User extends AbstractEntityModelUlid
{
    #[ORM\Column(type: 'string', length: 255)]
    private string $name;

    #[ORM\Column(type: 'string', length: 255)]
    private string $email;

    #[ORM\JoinTable(name: 'users_accounts')]
    #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)]
    #[ORM\InverseJoinColumn(name: 'account_code', referencedColumnName: 'code', nullable: false)]
    #[ORM\ManyToMany(targetEntity: Account::class)]
    private Collection $accounts;

    #[ORM\OneToMany(targetEntity: Post::class, mappedBy: 'user')]
    private Collection $posts;

    public function __construct()
    {
        parent::__construct();
        $this->accounts = new ArrayCollection();
        $this->posts = new ArrayCollection();
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;
        return $this;
    }

    public function getAccounts(): Collection
    {
        return $this->accounts;
    }

    public function getPosts(): Collection
    {
        return $this->posts;
    }
}



namespace AppModule;

use AppModule\Controllers\IndexController;
use GPDCore\Core\AbstractModule;
use GPDCore\Routing\RouteModel;

class AppModule extends AbstractModule
{
    public function getRoutes(): array
    {
        return [
            // Ruta simple
            new RouteModel('GET', '/index/{id:.+}', IndexController::class),
            
            // Más rutas
            new RouteModel('POST', '/users', UserController::class),
            new RouteModel('GET', '/users/{id:\d+}', UserDetailController::class),
            new RouteModel('PUT', '/users/{id:\d+}', UserUpdateController::class),
            new RouteModel('DELETE', '/users/{id:\d+}', UserDeleteController::class),
        ];
    }
}



namespace AppModule\Controllers;

use GPDCore\Routing\AbstractAppController;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class IndexController extends AbstractAppController
{
    public function dispatch(ServerRequestInterface $request): ResponseInterface
    {
        // Obtener parámetros de ruta
        $id = $this->getRouteParam("id");
        
        // Obtener query parameters (?key=value)
        $queryParams = $request->getQueryParams();
        $specificParam = $queryParams["queryParams"] ?? "";
        
        // Obtener el body del request (POST/PUT)
        $body = $request->getParsedBody();
        
        // Obtener headers
        $headers = $request->getHeaders();
        $authHeader = $request->getHeaderLine('Authorization');
        
        // Crear respuesta JSON
        return $this->jsonResponse([
            'success' => true,
            'data' => [
                'id' => $id,
                'queryParams' => $specificParam,
                'message' => 'Procesado correctamente'
            ]
        ], 200);
    }
}

// Obtener parámetros de ruta
$this->getRouteParam("id");
$this->getRouteParam("slug");

// Crear respuesta JSON
$this->jsonResponse(['data' => $result], 200);

// Crear respuesta de error
$this->jsonResponse(['error' => 'Not found'], 404);

// Acceder al EntityManager
$entityManager = $this->getEntityManager();

// Acceder al ServiceManager
$serviceManager = $this->getServiceManager();

// Acceder al contexto de la aplicación
$context = $this->getAppContext();

public function getRoutes(): array
{
    return [
        new RouteModel('GET', '/api/users', UserListController::class),
        new RouteModel('GET', '/api/users/{id:\d+}', UserDetailController::class),
        new RouteModel('POST', '/api/users', UserCreateController::class),
        new RouteModel('PUT', '/api/users/{id:\d+}', UserUpdateController::class),
        new RouteModel('DELETE', '/api/users/{id:\d+}', UserDeleteController::class),
    ];
}



namespace AppModule\Controllers;

use AppModule\Entities\User;
use GPDCore\Routing\AbstractAppController;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class UserListController extends AbstractAppController
{
    public function dispatch(ServerRequestInterface $request): ResponseInterface
    {
        $queryParams = $request->getQueryParams();
        $page = (int)($queryParams['page'] ?? 1);
        $limit = (int)($queryParams['limit'] ?? 10);
        
        $em = $this->getEntityManager();
        $repository = $em->getRepository(User::class);
        
        $users = $repository->findBy(
            [],
            ['createdAt' => 'DESC'],
            $limit,
            ($page - 1) * $limit
        );
        
        return $this->jsonResponse([
            'data' => array_map(fn($user) => [
                'id' => $user->getId(),
                'name' => $user->getName(),
                'email' => $user->getEmail()
            ], $users),
            'page' => $page,
            'limit' => $limit
        ]);
    }
}



namespace AppModule\Controllers;

use AppModule\Entities\User;
use GPDCore\Routing\AbstractAppController;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class UserDetailController extends AbstractAppController
{
    public function dispatch(ServerRequestInterface $request): ResponseInterface
    {
        $id = $this->getRouteParam("id");
        
        $em = $this->getEntityManager();
        $user = $em->find(User::class, $id);
        
        if (!$user) {
            return $this->jsonResponse([
                'error' => 'User not found'
            ], 404);
        }
        
        return $this->jsonResponse([
            'data' => [
                'id' => $user->getId(),
                'name' => $user->getName(),
                'email' => $user->getEmail(),
                'createdAt' => $user->getCreatedAt()->format('Y-m-d H:i:s')
            ]
        ]);
    }
}

// Resolver básico
'Query::getUsers' => ResolverFactory::forConnection(User::class)

// Con modificador de query personalizado
'Query::getActiveUsers' => ResolverFactory::forConnection(
    User::class, 
    new class implements QueryModifierInterface {
        public function modify(QueryBuilder $qb, array $args): QueryBuilder {
            return $qb->andWhere('entity.status = :status')
                     ->setParameter('status', 'active');
        }
    }
)

'Query::getUser' => ResolverFactory::forItem(User::class)

'Mutation::createUser' => ResolverFactory::forCreate(User::class)

'Mutation::updateUser' => ResolverFactory::forUpdate(User::class)

'Mutation::deleteUser' => ResolverFactory::forDelete(User::class)

use GPDCore\DataLoaders\EntityDataLoader;

$userDataLoader = new EntityDataLoader(User::class, $entityManager);

// En el módulo
'Post::author' => ResolverFactory::forEntity($userDataLoader, 'author')

'User::posts' => ResolverFactory::forCollection(User::class, 'posts', Post::class)
'User::activePosts' => ResolverFactory::forCollection(
    User::class, 
    'posts', 
    Post::class,
    new class implements QueryModifierInterface {
        public function modify(QueryBuilder $qb, array $args): QueryBuilder {
            return $qb->andWhere('target.status = :status')
                     ->setParameter('status', 'published');
        }
    }
)

// Middleware de ejemplo
$authMiddleware = fn($resolver) => fn($root, $args, $context, $info) => {
    if (!$context->isAuthenticated()) {
        throw new UnauthorizedException('Authentication ver($root, $args, $context, $info);
    $duration = microtime(true) - $startTime;
    error_log("Resolver {$info->fieldName} executed in {$duration}s");
    return $result;
};

// Aplicar middlewares (se ejecutan en orden inverso)
'Query::protectedData' => ResolverPipelineFactory::createPipeline($baseResolver, [
    ResolverPipelineFactory::createWrapper($loggingMiddleware),
    ResolverPipelineFactory::createWrapper($authMiddleware),
])

$cacheMiddleware = fn($resolver) => fn($root, $args, $context, $info) => {
    $cacheKey = "resolver_{$info->fieldName}_" . md5(serialize($args));
    
    if ($cached = $context->getCache()->get($cacheKey)) {
        return $cached;
    }
    
    $result = $resolver($root, $args, $context, $info);
    $context->getCache()->set($cacheKey, $result, 300); // 5 min
    
    return $result;
};

$wrappedMiddleware = ResolverPipelineFactory::createWrapper($cacheMiddleware);

use GPDCore\Graphql\ResolverFactory;
use GPDCore\Graphql\ResolverPipelineFactory;
use GPDCore\Graphql\ResolverTransactionMiddlewareFactory;

'Mutation::createUser' => ResolverPipelineFactory::createPipeline(
    ResolverFactory::forCreate(User::class),
    [
        ResolverTransactionMiddlewareFactory::createMiddleware(),
    ]
),

'Mutation::createUser' => ResolverPipelineFactory::createPipeline(
    ResolverFactory::forCreate(User::class),
    [
        // 3° en ejecutarse (más interno, justo antes del resolver): autorización
        ResolverPipelineFactory::createWrapper($authMiddleware),
        // 2° en ejecutarse: validación
        ResolverPipelineFactory::createWrapper($validationMiddleware),
        // 1° en ejecutarse (más externo): envuelve toda la operación dentro de la transacción
        ResolverTransactionMiddlewareFactory::createMiddleware(),
    ]
),

public function getTypes(): array
{
    return [
        DateType::NAME => DateType::class,
        DateTimeType::NAME => DateTimeType::class,
        JSONData::NAME => JSONData::class,
        // Tus tipos personalizados
        'MyCustomType' => MyCustomType::class,
    ];
}

// modules/AppModule/src/AppModule.php
class AppModule extends AbstractModule
{
    public function getResolvers(): array
    {
        return [
            // Consultas básicas
            'Query::getPosts' => ResolverFactory::forConnection(Post::class),
            'Query::getPost' => ResolverFactory::forItem(Post::class),
            'Query::getUsers' => ResolverFactory::forConnection(User::class),
            'Query::getUser' => ResolverFactory::forItem(User::class),
            
            // Mutaciones
            'Mutation::createPost' => ResolverFactory::forCreate(Post::class),
            'Mutation::updatePost' => ResolverFactory::forUpdate(Post::class),
            'Mutation::deletePost' => ResolverFactory::forDelete(Post::class),
            
            // Relaciones (prevención N+1)
            'Post::author' => ResolverFactory::forEntity(
                new EntityDataLoader(User::class, $this->entityManager), 
                'author'
            ),
            'User::posts' => ResolverFactory::forCollection(
                User::class, 
                'posts', 
                Post::class
            ),
            
            // Resolver personalizado con middleware
            'Mutation::publishPost' => ResolverPipelineFactory::createPipeline(
                function($root, $args, AppContextInterface $context, $info) {
                    $post = $context->getEntityManager()
                        ->getRepository(Post::class)
                        ->find($args['id']);
                    
                    if (!$post) {
                        throw new \Exception('Post not found');
                    }
                    
                    $post->setStatus('published');
                    $context->getEntityManager()->flush();
                    
                    return $post;
                },
                [
                    ResolverPipelineFactory::createWrapper($this->getAuthMiddleware()),
                    ResolverPipelineFactory::createWrapper($this->getOwnershipMiddleware()),
                ]
            ),
        ];
    }
    
    private function getAuthMiddleware(): callable
    {
        return fn($resolver) => fn($root, $args, $context, $info) => {
            if (!$context->getCurrentUser()) {
                throw new \Exception('Authentication 

// Evita el problema N+1
class UserResolvers
{
    private EntityDataLoader $userDataLoader;
    
    public function __construct(EntityManager $em)
    {
        $this->userDataLoader = new EntityDataLoader(User::class, $em);
    }
    
    public static function getPostsAuthorResolver(): callable 
    {
        return ResolverFactory::forEntity($this->userDataLoader, 'author');
    }
}

use GPDCore\Exceptions\GQLException;

'Query::sensitiveData' => function($root, $args, AppContextInterface $context, $info) {
    try {
        if (!$context->getCurrentUser()) {
            throw new GQLException('Not authenticated', 'UNAUTHENTICATED');
        }
        
        if (!$context->getCurrentUser()->hasRole('admin')) {
            throw new GQLException('Insufficient permissions', 'FORBIDDEN');
        }
        
        return $this->getSensitiveData();
        
    } catch (\Exception $e) {
        throw new GQLException(
            'Failed to fetch sensitive data: ' . $e->getMessage(),
            'INTERNAL_ERROR'
        );
    }
}

mi-proyecto/
├── config/
│   ├── master.config.php
│   ├── doctrine.local.php
│   └── doctrine.entities.php
├── data/
│   └── DoctrineORMModule/
├── modules/
│   └── AppModule/
│       ├── config/
│       │   ├── module.config.php
│       │   └── schema.graphql
│       └── src/
│           ├── AppModule.php
│           ├── Entities/
│           ├── Graphql/
│           └── Services/
├── public/
│   └── index.php
├── cli-config.php
└── composer.json
bash
# Servidor de desarrollo PHP
php -S localhost:8000 public/index.php


Request
  └─► TransactionMiddleware::beginTransaction()
        └─► resolver($root, $args, $context, $info)
              ├─ Éxito → commit() → return $result
              └─ Excepción → rollBack() → re-lanza la excepción

modules/AppModule/
├── config/
│   ├── module.config.php
│   └── schema.graphql
├── src/
│   ├── AppModule.php
│   ├── Entities/
│   │   ├── User.php
│   │   ├── Post.php
│   │   └── Comment.php
│   ├── Graphql/
│   │   ├── Resolvers/
│   │   │   ├── UserResolvers.php
│   │   │   ├── PostResolvers.php
│   │   │   └── CommentResolvers.php
│   │   ├── Types/
│   │   │   └── CustomScalar.php
│   │   └── Middleware/
│   │       ├── AuthMiddleware.php
│   │       └── RateLimitMiddleware.php
│   └── Services/
│       ├── UserService.php
│       └── PostService.php