How to get custom directive arguments in lighthouse graphql laravel - php

My question is regarding the custom directives
https://lighthouse-php.com/4.11/the-basics/directives.html#definition
My schema is:
type Query {
sayFriendly: String #append(text: ", please.")
}
in lighthouse.php the config, I already have
'namespaces' => [
'models' => ['App', 'App\\Models'],
'queries' => 'App\\GraphQL\\Queries',
'mutations' => 'App\\GraphQL\\Mutations',
'subscriptions' => 'App\\GraphQL\\Subscriptions',
'interfaces' => 'App\\GraphQL\\Interfaces',
'unions' => 'App\\GraphQL\\Unions',
'scalars' => 'App\\GraphQL\\Scalars',
'directives' => ['App\\GraphQL\\Directives'],
],
directive enabled.
I defined the directive at \App\GraphQL\Directives\appendDirective as
<?php
namespace App\GraphQL\Directives;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\Directive;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
class appendDirective implements Directive, FieldMiddleware
{
/**
* Name of the directive as used in the schema.
*
* #return string
*/
public function name(): string
{
return 'append';
}
/**
* Wrap around the final field resolver.
*
* #param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
* #param \Closure $next
* #return \Nuwave\Lighthouse\Schema\Values\FieldValue
*/
public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
{
// Retrieve the existing resolver function
/** #var Closure $previousResolver */
$previousResolver = $fieldValue->getResolver();
// Wrap around the resolver
$wrappedResolver = function ($root, array $args, GraphQLContext $context, ResolveInfo $info) use ($previousResolver): string {
// Call the resolver, passing along the resolver arguments
/** #var string $result */
$result = $previousResolver($root, $args, $context, $info);
return ($result);
};
// Place the wrapped resolver back upon the FieldValue
// It is not resolved right now - we just prepare it
$fieldValue->setResolver($wrappedResolver);
// Keep the middleware chain going
return $next($fieldValue);
}
}
how can I get the value of key "text" from my directive and append to $result [ #append(text: ", please.") ].
The $args is an empty array (and It should be because I did make parameterized query [sayFriendly] )

If you extend your directive from Nuwave\Lighthouse\Schema\Directives\BaseDirective you can have access to $this->directiveArgValue('text'); to retrieve the text argument to your custom directive.
The $args are empty because that are the arguments supplied by the client in the query, in the sayFriendly query example there are no args possible so that will always be empty.
As a tip: you can find this out by looking at an already implemented directives inside Lighthouse, the documentation is a bit thin on the custom directives but you can learn a lot by looking at the directives provided by Lighthouse.

Related

laravel 5.4 modify data before validation in request [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
I have my custom Request, which extends the Backpack CrudController.
Now I would like to override the prepareForValidation of the ValidatesWhenResolvedTrait since it looks like the right place to modify my incoming data, but I can't figure out how ...
So my first question is, can I override this method? Its protected ...
protected function prepareForValidation()
And my second question, how can I modify my input on the Request or FormRreuqest objects?
Here is my RequestClass
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Config;
class DonationsRequest extends \Backpack\CRUD\app\Http\Requests\CrudRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// only allow updates if the user is logged in
return \Auth::check();
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|max:255',
'email' => 'required|email',
'dob' => 'required|date',
'newsletter' => 'required|boolean',
'country' => 'sometimes|required|in:'.implode(',', Config::get('validation.countries')),
'street' => 'sometimes|required|string|max:255',
'zip' => 'sometimes|required|string|between:4,5',
'city' => 'sometimes|required|string|between:4,255',
'amount' => 'required|numeric|between:1,'.Config::get('donations.max'),
'type' => 'required|in:oo,monthly',
'provider' => 'sometimes|string|nullable',
'product_id' => 'sometimes|exists:products,id|nullable',
'campaign_id' => 'required|exists:campaigns,id',
'status' => 'sometimes|required|in:pending,canceled,success,error',
'profile' => 'sometimes|string|regex:/^profile[0-9]+$/|nullable',
];
}
/**
* Get the validation attributes that apply to the request.
*
* #return array
*/
public function attributes()
{
return [
//
];
}
/**
* Get the validation messages that apply to the request.
*
* #return array
*/
public function messages()
{
return [
//
];
}
private function prepareForValidation()
{
dd('getValidatorInstance custom');
$this->sanitizeInput();
return parent::getValidatorInstance();
}
private function sanitizeInput()
{
dd('sanitizeInput custom');
$data = $this->all();
dd($data);
// overwrite the newsletter field value to match boolean validation
$data['newsletter'] = ($data['newsletter'] == 'true' || $data['newsletter'] == '1' || $data['newsletter'] == true) ? true : false;
return $data;
}
private function validate() {
dd('validate');
}
}
As you can see, I first tried to override the getValidatorInstance method, since this looked like the common aproach to this, but it is not executed (so not overridden - protected?).
Although I didn't tried but it seems it should work you can override validationData from Illuminate\Foundation\Http\FormRequest class like.
/**
* Get data to be validated from the request.
*
* #return array
*/
protected function validationData()
{
$all = parent::validationData();
//e.g you have a field which may be json string or array
if (is_string($playerIDs = array_get($all, 'player_id')))
$playerIDs = json_decode($playerIDs, true);
$all['player_id'] = $playerIDs
return $all;
}
or you can override all method in Illuminate\Http\Concerns\InteractsWithInput trait
/**
* Get all of the input and files for the request.
*
* #return array
*/
public function all()
{
$all = parent::all();
//then do your operation
if (is_string($playerIDs = array_get($all, 'player_id')))
$playerIDs = json_decode($playerIDs, true);
$all['player_id'] = $playerIDs
return $all;
}
Could you modify the request?
$request->merge(['field' => 'new value']);
Well I am sure,this can help in modifying The input, it worked for me.[laravel 5.4]
place this
$input['url'] = $url;
$this->replace($input);
dd($input);
in listFormRequest. (use $all instead of $input, if you follow above used answer).
This only changes input,which is available even in controller. You still need to find a way to insert it into DB, or do something else to use modified input for using it in blade.
Ok I found out where the error was. I did split the Frontend Request and the Backend Request Call. Since I was working on the Backend Request the Frontend Request was not overwriting anything ... so it was my bad, no bug there, sry for the waste of time, but a big thanks to the community!

TYPO3 ExtBase - Required argument X is not set

I try to modify the extension importr to insert a custom action to the controller "Importr" at importr\Classes\Controller\ImportrController.php.
I called the action "customAction" and reference to it from a button which I render with fluid in importr\Resources\Private\Templates\Importr\Index.html by using <f:link.action>
<div id="myButton">
<f:link.action
extensionName="Importr"
pluginName="Importr"
controller="Importr"
action="custom"
arguments="{taskid:5}"
>
Click here
</f:link.action>
</div>
controller action:
/**
* #param int $taskid
* #return void
*/
public function customAction($taskid)
{
...
}
However, every attempt to pass a parameter to the action fails.
At my first attempt I even get an error page without even clicking on the button as you can see in the screenshot below.
Attempt #1
/**
* #param int $taskid
* #return void
*/
public function customAction($taskid)
{
print_r($taskid);
die;
}
Uncaught TYPO3 Exception
1298012500: Required argument "taskid" is not set for HDNET\Importr\Controller\ImportrController->custom.
TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException
thrown in file
/var/www/typo3/typo3_src-6.2.25/typo3/sysext/extbase/Classes/Mvc/Controller/AbstractController.php
in line 425.'
Attempt #2
/**
* #return void
*/
public function customAction()
{
$taskid = "default";
if ($this->request->hasArgument('taskid')) {
$taskid = $this->request->getArgument('taskid');
}
echo "TASKID = '$taskid'";
die;
...
OUTPUT: TASKID = 'default'
Attempt #3:
/**
* #return void
*/
public function customAction()
{
$args = $this->request->getArguments();
$taskid = $args['taskid'];
echo "TASKID = '$taskid'";
die;
OUTPUT: TASKID = ''
I don't know what else I can try. Is it possible that I made a mistake in the Fluid Code? Do I use a wrong pluginName or extensionName or is it even a typo3 bug? Where is the pluginName stored so I can check it?
More Infos
I allowed my custom action by adding it to the other actions inside ext_tables.php
<?php
if (!defined('TYPO3_MODE')) {
die('Access denied.');
}
/** #var string $_EXTKEY */
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
'HDNET.' . $_EXTKEY,
'file',
'tx_importr_mod',
'',
[
'Importr' => 'custom,index,import,preview,create',
],
[
'access' => 'user,group',
'icon' => 'EXT:' . $_EXTKEY . '/ext_icon.gif',
'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xlf'
]);
There were two problems:
1. ext_tables.php
The order of the actions was not correct.
The first action is always the default action, so it took my custom action as default action and therefor no page was loaded after clicking on the Importr Modul in the menu at the left. It worked after I corrected it.
'Importr' => 'index,import,preview,create,custom',
2. Fluid
The pluginName was incorrect, the correct pluginName is file_importrtximportrmod, which we can see in ext_tables.php
It is not even needed, it also works if you omit pluginName and extensionName.
Make sure to clear the cache in the install tool afterwards.

Laravel 5: Odd "Undefined Variable" error when returning a defined array to View

As the title states, I'm getting an odd error in Laravel 5. I'm new to Laravel, and this week I dived into Jobs/Queues. I've gotten an "Undefined Variable: $errors" error in the past, and that one I was able to understand and fix. But now, I can't seem to get past this one. To my knowledge, everything looks fine. The following breakdown will (hopefully) give you an idea of what I'm doing/where the error happens:
class PostFormFields extends Job implements SelfHandling
{
use InteractsWithQueue, SerializesModels;
/**
* The id (if any) of the Post row
*/
protected $id;
/**
* List of fields and default value for each field
*/
protected $fieldList = [
'title' => '',
'subtitle' => '',
'page_image' => '',
'content' => '',
'meta_description' => '',
'is_draft' => '8',
'publish_date' => '',
'publish_time' => '',
'layout' => 'blog.layouts.post',
'tags' => [],
];
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($id = null)
{
$this->id = $id;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$fields = $this->fieldList;
if($this->id)
{
$fields = $this->fieldsFromModel($this->id, $fields);
} else {
$when = Carbon::now()->addHour();
$fields['publish_date'] = $when->format('M-j-Y');
$fields['publish_time'] = $when->format('g:i A');
}
/**
* Populate with old values, if they exist
* #var [type]
*/
foreach ($fields as $fieldName => $fieldValue)
{
$fields[$fieldName] = old($fieldName, $fieldValue);
}
$fields = array_merge($fields, ['allTags' => Tag::lists('tag')->all()]);
return $fields;
}
Above is the code inside the handler function of my Job class, the file it sits in is called PostFormFields.php. It's job, essentially, is just to return an array filled with all the values pertaining to a post, based on the Post Model and what's in the database that pertains to that specific Post ('title','content',etc) if a user's entered them in the past
public function create()
{
$data = $this->dispatch(new PostFormFields());
$data['title'] = 'testing';
var_dump($data);
return view('admin.post.create', $data);
}
Above is the code inside my PostController class, in the create() method. As you can tell, I'm using a resource controller for my Post Controller. It dispatches the PostFormFields Job and stores all the returned data in an array $data. However, since the create() method will be used to create a new post, only the keys should be returned, with values set to their default value ''.
This works. As you can see, i run a 'var_dump()' on the variable $data to see what, if anything, is returned. I then pass the $data array to the create View. This is where the error comes up.
Laravel "Undefined Varieble" Error
Above is a picture of the error I get when I try to access the /create route. It's clear that the $data does have the $title variable defined, as well as all the other keys in the array. Why am I getting an "Undefined Variable" array when I clearly have it defined by the time it's sent to the create View?
The line of code is says the error is in is the following:
<input type="text" class="radius" name="title" id="title" value="{{ $title }}">
You have to pass that array to view via compact function of laravel. So that you can use it in view as you want.
Please check about compact here - https://laracasts.com/discuss/channels/general-discussion/phps-compact-pros-and-cons?page=1
public function create()
{
$data = $this->dispatch(new PostFormFields());
$data['title'] = 'testing';
var_dump($data);
return view('admin.post.create', compact('data'));
}

How to include fractal transformed objects directly to collection meta without data key

I'm using league/fractal with JsonApiSerializer,
I've got users collection for json output.
Now I want to add some filters data to this json response (like users count for current filters).
I got this:
$resource = new Collection($dataProvider->getData(), new UserTransformer());
//the only way to include some not directly linked data i found is using setMeta():
$resource->setMetaValue('projects', $dataProvider->getProjects());
$resource->setMetaValue('somes', $dataProvider->getTasks());
But! 'projects' & 'somes' collections (yes, they are collection too) also included with 'data' key in it.
So, I've got this structure:
{
'data' => [
{//user1},{//user2},...
],
'meta' => {
'projects' => {
'data' => {...}
},
'somes' => {
'data' => {...}
}
}
}
but I want something like:
{
'data' => [
{//user1},{//user2},...
],
'meta' => {
'projects' => {...}, //there is no 'data' key
'somes' => {...} //there is no 'data' key
}
}
What should I do?
This is kinda hack but works fine without refactor Scope class which hardcoded in fractal's League\Fractal\Manager::createData() and is only way to use your own Scope class realization is to overload this method in Manager's extension.
<?php
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class EmbedSerializer
*/
class EmbedSerializer extends JsonApiSerializer
{
const RESOURCE_EMBEDDED_KEY = 'embedded';
/**
* Serialize a collection.
*
* #param string $resourceKey
* #param array $data
* #return array
*/
public function collection($resourceKey, array $data)
{
return $resourceKey === self::RESOURCE_EMBEDDED_KEY ? $data : [$resourceKey ?: 'data' => $data];
}
/**
* Serialize an item.
*
* #param string $resourceKey
* #param array $data
* #return array
*/
public function item($resourceKey, array $data)
{
return $resourceKey === self::RESOURCE_EMBEDDED_KEY ? $data : [$resourceKey ?: 'data' => [$data]];
}
}
So, now i could use it like:
/** #var $this->fractal League\Fractal\Manager */
$this->fractal->setSerializer(new EmbedSerializer());
$projectsCollection = $this->fractal->createData(
new Collection($projects, new UserProjectTransformer(), 'embedded')
)->toArray();
$resource = new Collection($users, new UserTransformer());
$resource->setMetaValue('projects', $projectsCollection);
That's all u need. Hope this will be helpful.

Symfomy2 manual route definitions with FOSRestBundle

I am now using the FOSRestBundle in order to build a REST API within my Symfony application. The idea for now is to list some locations(hotels, restaurants...), I managed to configure the automatic routes with FOSRestBundle like:
/api/locations , /api/locations/{id} , /api/locations/{name}/detail
with this controller:
class LocationController extends FOSRestController implements ClassResourceInterface
{
/**
* GET /locations
*
* #return Array
*
*/
public function cgetAction()
{
$locations = $this->getDoctrine()
->getManager()
->getRepository('VisitBILocationsBundle:Location')
->findAll();
if (!$locations) {
return array(
'locations' => $locations,
'status' => 1
);
}
return array(
'locations' => $locations,
'status' => 0
);
}
/**
* GET /locations/{locationId}
*
* #return Array
*
*/
public function getAction($id)
{
$location = $this->getDoctrine()
->getManager()
->getRepository('VisitBILocationsBundle:Location')
->findBy(array('id' => $id));
if (!$location) {
return array(
'location' => $location,
'status' => 1
);
}
return array(
'location' => $location,
'status' => 0
);
}
/**
* GET /locations/{name}/detail
*
* #return Array
*/
public function getDetailAction($name)
{
$detail = $this->getDoctrine()
->getManager()
->getRepository('VisitBILocationsBundle:LocationDetail')
->findBy(array('name' => $name));
if (!$detail) {
return array(
'locationDetail' => $detail,
'status' => 1
);
}
return array(
'locationDetail' => $detail,
'status' => 0
);
}
}
I've been struggling with this, but would anyone know how should I proceed to generate one custom url like this:
/api/locations/nearby/{latitude}/{longitude}
The idea is that I would provide my own latitude and longitude, and the backend will calculate and provide the locations which are the closest to me.
Of course I've looked at the documentation of FOSRestBundle for manual route configuration, but since I spent some time trying to do it, I come here to ask for some help :)
If you want to manually define a route, it should just be as simple as adding the route to the existing routing configuration. How exactly you do it depends on how you're handling the routing configuration: annotation, yaml, or xml.
Option 1: YAML
In the routing.yml file (ex: src/Vendor/MyBundle/Resources/config/routing.yml) add something like:
location_nearby:
pattern: /api/locations/nearby/{latitude}/{longitude}
defaults: { _controller: "MyBundle:Location:nearby" }
requirements:
_method: GET
which would correspond to this method in LocationController:
public function nearbyAction($latitude, $longitude) { ... }
Option 2: Annotations
Add this use statement to the Controller file:
use FOS\RestBundle\Controller\Annotations\Get;
and then define the route above the controller method:
/**
* Return a nearby location
* #Get("/api/locations/nearby/{latitude}/{longitude}")
*/
public function nearbyAction($latitude, $longitude) { ... }
OK here is how to proceed, works fine for me:
I use the annotation system to route /locations/nearby/{latitude}/{longitude}
/**
* Return a nearby location
* #Get("/locations/nearby/{latitude}/{longitude}", requirements={"latitude" = "[-+]?(\d*[.])?\d+", "longitude" = "[-+]?(\d*[.])?\d+"})
*/
public function nearbyAction($latitude, $longitude) {...}
Then I have to specify float numbers with: requirements={"latitude" = "[-+]?(\d*[.])?\d+", "longitude" = "[-+]?(\d*[.])?\d+"}
Those will still be interpreted as string by the controller: "64.1333", I just have to use this in the controller:
floatval($latitude)
to get url parameters as float and then do my calculations!

Categories