aimeos / aimeos-laravel

Laravel ecommerce package for ultra fast online shops, scalable marketplaces, complex B2B applications and #gigacommerce
https://aimeos.org/Laravel
MIT License
7.26k stars 1.05k forks source link

Lighthouse-php GraphQL compatibility? #414

Closed binaryfire closed 2 years ago

binaryfire commented 3 years ago

Hi guys

Will the Laravel Aimeos package work with the lighthouse-php GraphQL package?

Thanks!

aimeos commented 3 years ago

Lighthouse-php requires Eloquent models which we don't use for performance reasons. Thus, the package isn't compatible with Aimeos if you don't implement models for the tables Aimeos is using.

GraphQL is very good for write-heavy applications but as frontend for read-heavy e-commerce applications it's not suited and we recommend the JSON:API which scales infinitely compared to GraphQL.

binaryfire commented 3 years ago

@aimeos Sorry, just to clarify, this would be used for a custom admin panel. Not for the storefront API.

GraphQL could be quite good for this use case :)

aimeos commented 3 years ago

Yes, for the admin backend we are already working on a GraphQL interface. We've already played around with graphql-php package (https://webonyx.github.io/graphql-php/) to test the best way for relations and filtering but we lack some time at the moment. If you want to help, you are heartly invited to join :-)

<?php

require_once( '../vendor/autoload.php' );

use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\InputObjectField;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;

class Registry
{
    private $types = [];
    private $resolve = [];
    private $args = [];

    public function add( $name, $type, $args, $resolve ) : self
    {
        $this->resolve[$name] = $resolve;
        $this->types[$name] = $type;
        $this->args[$name] = $args;

        return $this;
    }

    public function type( $name ) : ?Type
    {
        return $this->types[$name] ?? null;
    }

    public function args( $name ) : array
    {
        return $this->args[$name] ?? [];
    }

    public function resolve( $name ) : ?callable
    {
        return $this->resolve[$name] ?? null;
    }
}

$registry = new Registry();
$types = $support = [];

$support['media'] =  [
    'type' => new ObjectType( [
        'name' => 'media',
        'fields' => function() use ( $registry ) {
            return [
                'id' => Type::string(),
                'siteid' => Type::string(),
                'domain' => Type::string(),
                'type' => Type::string(),
                'url' => Type::string(),
                'preview' => Type::string(),
                'previews' => Type::listOf( new ObjectType( [
                    'name' => 'previews',
                    'fields' => [
                        'width' => Type::int(),
                        'url' => Type::string()
                    ],
                    'resolveField' => function( $entry, $args, $context, ResolveInfo $info ) {
                        switch( $info->fieldName ) {
                            case 'width': return key( $entry );
                            case 'url': return current( $entry );
                        }
                        return null;
                    }
                ] ) ),
                'status' => Type::int(),
                'ctime' => Type::string(),
                'mtime' => Type::string(),
                'editor' => Type::string(),
            ];
        },
        'resolveField' => function( $mediaItem, $args, $context, ResolveInfo $info ) {
            // return $mediaItem->get( 'media.' . $info->fieldName );
            return $mediaItem['media.' . $info->fieldName] ?? null;
        }
    ] ),
    'args' => [
        'type' => Type::string(),
    ],
    'resolve' => function( $listItem, $args, $context, ResolveInfo $info ) {
        // return $listItem->getRefItem();
        return $listItem['media'] ?? [];
    }
];

$support['lists'] = [
    'type' => Type::listOf( new ObjectType( [
        'name' => 'lists',
        'fields' => function() use ( $registry ) {
            return [
                'id' => Type::string(),
                'siteid' => Type::string(),
                'domain' => Type::string(),
                'type' => Type::string(),
                'refid' => Type::string(),
                'datestart' => Type::string(),
                'dateend' => Type::string(),
                'status' => Type::int(),
                'ctime' => Type::string(),
                'mtime' => Type::string(),
                'editor' => Type::string(),
                'media' => [
                    'type' => $registry->type( 'media' ),
                    'args' => $registry->args( 'media' ),
                    'resolve' => $registry->resolve( 'media' ),
                ]
            ];
        },
        'resolveField' => function( $listItem, $args, $context, ResolveInfo $info ) {
            $prefix = join( '.', array_slice( $info->path, -4, 2 ) );
            // return $listItem->get( $prefix . '.' . $info->fieldName );
            return $listItem[$prefix . '.' . $info->fieldName] ?? null;
        }
    ] ) ),
    'args' => [
        'domain' => Type::string(),
        'type' => Type::string(),
    ],
    'resolve' => function( $item, $args, $context, ResolveInfo $info ) {
        // return $item->getListItems()
        return $item['lists'] ?? [];
    }
];

$types['product'] = [
    'type' => new ObjectType( [
        'name' => 'product',
        'fields' => function() use ( $registry ) {
            return [
                ['name' => 'id', 'type' => Type::string(), 'description' => 'Unique ID'],
                'siteid' => Type::string(),
                'code' => Type::string(),
                'label' => Type::string(),
                'datestart' => Type::string(),
                'dateend' => Type::string(),
                'status' => Type::int(),
                'ctime' => Type::string(),
                'mtime' => Type::string(),
                'editor' => Type::string(),
                'rating' => Type::string(),
                'ratings' => Type::int(),
                'lists' => [
                    'type' => $registry->type( 'lists' ),
                    'args' => $registry->args( 'lists' ),
                    'resolve' => $registry->resolve( 'lists' )
                ]
            ];
        },
        'resolveField' => function( $productItem, $args, $context, ResolveInfo $info ) {
            // return $productItem->get( $info->parentType->name . '.' . $info->fieldName );
            return $productItem[$info->parentType->name . '.' . $info->fieldName] ?? null;
        }
    ] ),
    'args' => [
        ['name' => 'filter', 'type' => Type::string(), 'defaultValue' => '{}', 'description' => 'Filter description'],
        ['name' => 'include', 'type' => Type::string(), 'defaultValue' => '{}', 'description' => 'Include description'],
        ['name' => 'sort', 'type' => Type::listOf(Type::string()), 'defaultValue' => [], 'description' => 'Sort description'],
        ['name' => 'offset', 'type' => Type::int(), 'defaultValue' => 0, 'description' => 'Offset description'],
        ['name' => 'limit', 'type' => Type::int(), 'defaultValue' => 0, 'description' => 'Limit description'],
    ],
    'resolve' => function( $root, $args, $context, ResolveInfo $info ) {
        return [
            'product.id' => '1',
            'product.siteid' => '1.',
            'product.code' => 'test',
            'product.label' => 'Test product',
            'product.datestart' => null,
            'product.dateend' => null,
            'product.status' => 0,
            'product.ctime' => '2000-01-01 00:00:00',
            'product.mtime' => '2000-01-01 00:00:00',
            'product.editor' => 'aimeos',
            'product.rating' => '5.00',
            'product.ratings' => 1,
            'lists' => [[
                'product.lists.id' => '1',
                'product.lists.siteid' => '1.',
                'product.lists.domain' => 'media',
                'product.lists.type' => 'default',
                'product.lists.refid' => '123',
                'product.lists.datestart' => null,
                'product.lists.dateend' => null,
                'product.lists.status' => 1,
                'product.lists.ctime' => '2000-01-01 00:00:00',
                'product.lists.mtime' => '2000-01-01 00:00:00',
                'product.lists.editor' => 'aimeos',
                'media' => [
                    'media.id' => '1',
                    'media.siteid' => '1.',
                    'media.domain' => 'media',
                    'media.type' => 'default',
                    'media.url' => 'path/to/image1.jpg',
                    'media.preview' => 'path/to/image2.jpg',
                    'media.previews' => [
                        [250 => 'path/to/image3.jpg'],
                        [500 => 'path/to/image4.jpg'],
                    ],
                    'media.status' => 1,
                    'media.ctime' => '2000-01-01 00:00:00',
                    'media.mtime' => '2000-01-01 00:00:00',
                    'media.editor' => 'aimeos',
                ]
            ]]
        ];
    }
];

foreach( $support as $name => $entry) {
    $registry->add( $name, $entry['type'], $entry['args'] ?? [], $entry['resolve'] ?? null );
}

foreach( $types as $name => $entry) {
    $registry->add( $name, $entry['type'], $entry['args'] ?? [], $entry['resolve'] ?? null );
}

$schema = new Schema([
    'query' => new ObjectType( [
        'name' => 'query',
        'fields' => $types
    ] ),
]);

try
{
    $rawInput = file_get_contents('php://input');

    $input = json_decode($rawInput, true);
    $query = $input['query'] ?? null;
    $root = [];
    $context = null;
    $variableValues = $input['variables'] ?? null;
    $operationName = $input['operationName'] ?? null;

    $output = GraphQL::executeQuery( $schema, $query, $root, $context, $variableValues, $operationName )->toArray();
}
catch (\Exception $e)
{
    $output = [
        'errors' => [[
            'message' => $e->getMessage()
        ]]
    ];
}

header('Content-Type: application/json');
echo json_encode($output);
aimeos commented 2 years ago

A GraphQL API for administration is now included in Aimeos 2022.10.1+