Giter VIP home page Giter VIP logo

Comments (13)

kikoseijo avatar kikoseijo commented on July 19, 2024 1

By the way, something I found interesting was to create 3 traits depending on the way the data its sent and to be able to use interactions with or without relay, I did;:

namespace App\Interactions\Traits;

trait RelayCreateHandler
{
    public $isRelay = false;
    public $responseMsg = '';

    public function relayValidator(array $data)
    {
        $payload = array_get($data, 'payload');

        if (!$payload) {
            throw new ValidationError('Form should be wrapped in a payload.');
        }

        return $this->validator($payload);
    }

    public function relayHandle(array $data)
    {
        $this->isRelay = true;

        return $this->handle(array_get($data, 'payload'));
    }

    public function relayResponse($payload = [], $success = true)
    {
        return [
            'success' => $success,
            'msg' => $this->responseMsg,
            'payload' => json_encode($payload),
        ];
    }
}

namespace App\Interactions\Traits;

trait RelayPureHandler
{
    public $isRelay = false;
    public $responseMsg = '';

    public function relayValidator(array $data)
    {
        return $this->validator($data);
    }

    public function relayHandle(array $data)
    {
        $this->isRelay = true;

        return $this->handle($data);
    }

    public function relayResponse($payload = [], $success = true)
    {
        return [
            'success' => $success,
            'msg' => $this->responseMsg,
            'payload' => json_encode($payload),
        ];
    }
}
namespace App\Interactions\Traits;

trait RelayUpdateHandler
{
    protected $isRelay = false;
    protected $responseMsg = '';

    public function relayValidator(array $data)
    {
        $payload = array_get($data, 'payload');

        if (!$payload) {

            throw new ValidationError('Form should be wrapped in a payload.');
        }

        return $this->validator(auth()->id(), $payload);
    }

    public function relayHandle(array $data)
    {
        $this->isRelay = true;

        return $this->handle(auth()->id(), array_get($data, 'payload'));
    }

    public function relayResponse($success = true, $payload = [])
    {
        return [
            'success' => $success,
            'msg' => $this->responseMsg,
            'payload' => json_encode($payload),
        ];
    }
}

for this to work here its my interaction directive for graphql

namespace App\GraphQL\Directives;

use GraphQL\Language\AST\DirectiveNode;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
use Nuwave\Lighthouse\Support\Exceptions\ValidationError;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;

class InteractionDirective implements FieldResolver
{
    use HandlesDirectives;

    /**
     * Name of the directive.
     *
     * @return string
     */
    public function name()
    {
        return 'interaction';
    }

    /**
     * Resolve the field directive.
     *
     * @param FieldValue $value
     *
     * @return FieldValue
     */
    public function resolveField(FieldValue $value)
    {
        $directive = $this->fieldDirective($value->getField(), $this->name());
        $className = $this->getClassName($directive);
        $data = $this->argValue(collect($directive->arguments)->first(function ($arg) {
            return 'args' === data_get($arg, 'name.value');
        }));

        return $value->setResolver(function ($root, array $args, $context = null, $info = null) use ($className, $data) {

            return $this->interaction($className, [$args]);
        });
    }

    /**
     * Get class name for resolver.
     *
     * @param DirectiveNode $directive
     *
     * @return string
     */
    protected function getClassName(DirectiveNode $directive)
    {
        $class = $this->directiveArgValue($directive, 'class');

        if (!$class) {
            throw new DirectiveException(sprintf(
                'Directive [%s] must have a `class` argument.',
                $directive->name->value
            ));
        }

        return $class;
    }

    protected function interaction($interaction, array $parameters)
    {
        $validator = $this->call($interaction . '@relayValidator', $parameters);

        if ($validator->fails()) {
            throw with(new ValidationError('validation'))->setValidator($validator);
        }

        return $this->call($interaction, $parameters);
    }

    /**
     * Will call interacion handle function if no other method its defined.
     *
     * @param  string $interaction
     * @param  array  $parameters
     * @return mixed
     */
    protected function call($interaction, array $parameters = [])
    {
        if (!str_contains($interaction, '@')) {
            $interaction = $interaction . '@relayHandle';
        }

        list($class, $method) = explode('@', $interaction);

        $base = class_basename($class);

        return call_user_func_array([app($class), $method], $parameters);
    }

}

from lighthouse.

vladar avatar vladar commented on July 19, 2024 1

Keep in mind that there is a spec for file uploads which had been adopted by many client libraries already: https://github.com/jaydenseric/graphql-multipart-request-spec (see the list of available serverside implementations and client libs at the bottom).

I think it makes sense to conform to this spec. There is a standalone implementation for PHP (already mentioned above), but it works for PSR-7 requests only. On the other hand, it is not that big to implement separately if required (just two classes basically).

from lighthouse.

kikoseijo avatar kikoseijo commented on July 19, 2024

What about this https://github.com/Ecodev/graphql-upload
And here its the source webonyx/graphql-php#220

from lighthouse.

chrissm79 avatar chrissm79 commented on July 19, 2024

@hailwood Great question, I didn't realize there was some work being done in the GraphQL community on this (I've been using the REST method you mentioned as well). I'd definitely be interested in getting this worked into the package though!

It looks like the graphql-multipart-request-spec is currently the way to go so I'll add this to the roadmap. If anyone wants to create a PR for it I'd be happy to merge 😄

@kikoseijo Thanks for the suggestion, it looks like a good possible solution!!

from lighthouse.

spawnia avatar spawnia commented on July 19, 2024

Just throwing this in here, the folks over there have implemented file upload https://github.com/rebing/graphql-laravel

from lighthouse.

kikoseijo avatar kikoseijo commented on July 19, 2024

I made it work out of the box by processing file uploads with laravel and relay modern.

I can post some code tomorrow if any one needs, but didnt took long, was quite simple, single file upload.

from lighthouse.

hailwood avatar hailwood commented on July 19, 2024

@kikoseijo I'd be quite interested to see what you've got!

from lighthouse.

kikoseijo avatar kikoseijo commented on July 19, 2024

Here is the Relay Mutation

// @flow
import { commitMutation, graphql } from 'react-relay';
import environment from '@graphql/env';
import { type MediaUploadInput } from '@libs/types';

const mutation = graphql`
	mutation MediaUploadMutation($payload: MediaUploadInput!) {
		mediaUpload(payload: $payload) {
			success
			msg
			payload
		}
	}
`;

export default (
	payload: MediaUploadInput,
	file: *,
	onMutationSuccessCallback: Function,
	onMutationErrorCallback: Function
) => {
	const variables = { payload };
	const uploadables = { file: file };

	commitMutation(environment, {
		mutation,
		variables,
		uploadables,
		onCompleted: (response, error) => {
			if (response.mediaUpload && response.mediaUpload.success) {
				onMutationSuccessCallback(response.mediaUpload);
			} else {
				onMutationErrorCallback(error);
			}
		},
		onError: err => onMutationErrorCallback(err)
	});
};

React code, im usingmobx-react-form and `react-native-image-picker

MediaUploadMutation(form.values(), form.foto, this._handleSuccess, this._handleError);
form.foto = {
	uri: 'data:image/jpeg;base64,' + photoFile.data,
	type: photoFile.type ? photoFile.type : 'image/jpeg',
	name: photoFile.fileName ? photoFile.fileName : 'photo.jpg'
};

Now its the schema on Laravel

extend type Mutation @group(middleware: ["auth:api"]) {
  mediaUpload(payload: MediaUploadInput!): ResponsePayload
    @interaction(class: "App\\Interactions\\UserMediaContribution")
}

To finish the interaction using UserMediaContribution, but any request()->hasFile('file') will work

namespace App\Interactions;

use App\Enums\GalleryType;
use App\Repositories\GalleryRepository;
use Illuminate\Support\Facades\Validator;
use Nuwave\Lighthouse\Support\Exceptions\ValidationError;

class UserMediaContribution
{
    use RelayUpdateHandler;

    public function validator($id, array $data)
    {
        return Validator::make($data, [
            'title' => 'required|string|min:5|max:255',
            'body' => 'nullable|string|min:5|max:255',
        ]);
    }

    public function handle($id, array $data)
    {
        if (request()->hasFile('file')) {

            $contribGallery = Larapp::call(GalleryRepository::class . '@contribGalleryForApp', []);

            $name = array_get($data, 'title');
            $body = array_get($data, 'body');

            $contribGallery->addMediaFromRequest('file')
                ->usingName($name)
                ->withCustomProperties(['pass' => 'n', 'body' => $body, 'by' => $id])
                ->toMediaCollection(GalleryType::GA_CONTRIBUTIONS);

            if ($this->isRelay) {
                $this->responseMsg = 'La foto se ha recibido satisfactoriamente, será publicada despues de haberse aprobado.';

                return $this->relayResponse($data);
            }

            return $data;

        } else {

            throw new ValidationError('No Photo found in request, contact admin.');
        }

    }

}

So, basically the trick on relay its to use the uploadables section to send the files there, haven't tried with an array but should also work, same as laravel, them file data and handles them very easy.

from lighthouse.

alberthaff avatar alberthaff commented on July 19, 2024

I am working on a Lighthouse client for Angular.
File uploads is a must in my use case, so I will be adding the functionality to the client. Is there any updates on an official standard for file uploads, or are you still interested in PRs?

I'm currently just using a scalar in my backend to handle file uploads through multipart-requests, but it would be nice with an official standard.

from lighthouse.

kikoseijo avatar kikoseijo commented on July 19, 2024

Laravel or any of their plugins works out of the box.

from lighthouse.

alberthaff avatar alberthaff commented on July 19, 2024

@kikoseijo File uploads through a GraphQL mutation works out of the box?

from lighthouse.

kikoseijo avatar kikoseijo commented on July 19, 2024

I have been out for a little while, but, as i work, i process requests inside a method. Does not matter how you sending file to server, you can retrieve it anywhere using request()->file('file_name').

I invite you to make a PR for what you would like it happen.
This is definitely a community driven beauty.

from lighthouse.

alberthaff avatar alberthaff commented on July 19, 2024

@kikoseijo Will do.
Great. That is pretty much the same way I'm doing it within my scalar (it just replaces the filename with the actual file) on all attributes of type File.
I was just thinking that it would be an advantage for everyone, if we decided on an official way of uploading files so that third party libraries can use one standard, instead of having to conform to multiple ways of uploading files through mutations. :-)

from lighthouse.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.