Symfomy2 manual route definitions with FOSRestBundle - php

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!

Related

Alter data from database before returning to view

I want to alter the file_name data before returning it to the view. Currently i'm doing:
$ci = CI::where('user_id',Auth::id())->get();
return $$ci->map(function($item){
return collect([
'id' => $item->id,
'title' => $item->title,
'description' => $item->description,
'file_name' => $this->archiving->url.'/media/content/'.$item->file_name // the part i need to alter
]);
});
this works fine, but i need to put in the 'id,title,description'(basically all the other data that i'm not altering) or else the result will only give me the file_name. Is there a better way of doing this? Thanks
You can set Accessors & Mutators
class CI extends Model
{
/**
* Get the file path
*
* #param string $value
* #return string
*/
public function getFileNameAttribute($file_name)
{
return $this->archiving->url.'/media/content/'.$file_name;
}
}
Check more abount Accessors & Mutators

How to get custom directive arguments in lighthouse graphql laravel

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.

Symfony - render view by defined status

I defined status field in my db table like
$newPlay->setStatus(Plays::STATUS_PUBLISHED);
that is now saved in that db table.
I need to render a view with fields that have that status.
I made an api call for that but I keep getting an error
Controller "AppBundle\Website\Controller\PublishedTicketsController::publishedTicketsAction()" requires that you provide a value for the "$status" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
This is my api call..
/**
* #Route("/published-tickets", name="published_tickets")
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
* #throws \Exception
*/
public function publishedTicketsAction(Request $request, $status)
{
$query = $this->getDoctrine()
->getRepository('AppBundle:Plays')->findBy(['status' => $status]);
return $this->render('#FrontTemplates/pages/published-tickets.html.twig', array(
'query' => $query,
'status' => $status
));
}
I have done it like this..
In my service..
public function publishedTicket()
{
$query = $this->getPlaysRepository()
->createQueryBuilder('t')
->select('t')
->where('t.status =:status')
->setParameter('status', Plays::STATUS_PUBLISHED)
->getQuery()
->getResult();
return $query;
}
and in my controller
$playPublish = $this->container->get('publish.tickets')->publishedTicket();
return $this->render('#FrontTemplates/pages/published-tickets.html.twig', array(
'playPublish' => $playPublish
));
this is the part I could't figure out..
->setParameter('status', Plays::STATUS_PUBLISHED)

return object's properties from related data in symfony json result

I am attempting to return a list of comments related to a an entity. The query results on when it runs and returns, the related field does not provide a meaningful result.
Here is the comment entity declarations
/**
* #var Books
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Books")
*/
private $imagefk;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Users")
*/
private $userfk;
This is my controller snippets of codes that fetches all the comment a user commented to a particular book
private function serializeComments(Comments $cmt) {
return array(
'message' => $cmt->getMessage(),
'userid' => $cmt->getUserfk(),
'bookid' => $cmt->getBookfk(),
);
}
the below function calls the function above
public function getAllCommentsAction($books)
{
$messages = $em->getRepository("AppBundle")->findBy(
array(
"imagefk" => $books
)
);
$data = array();
foreach ($messages as $message)
{
array_push($data, $this->serializeComments($message));
}
$response = new Response(json_encode($data), 200);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
Here is the result of attempt
[{"message":"This is comment for a user one","userid":{"__initializer__":{},"__cloner__":{},"__isInitialized__":false},"bookid":{"path":"http:\/\/10.0.2.2:88\/xxx\/web\/uploads\/pdf\/5ub3uy8zv09cee2avi11.pdf"}}
Please how can I return the objects properties from this result instead of this
"userid":{"__initializer__":{},"__cloner__":{},"__isInitialized__":false},"bookid":{"path":"http:\/\/10.0.2.2:88\/xxx\/web\/uploads\/pdf\/5ub3uy8zv09cee2avi11.pdf"
Try accessing the object properties:
'userid' => $cmt->getUserfk()->getId(),
instead of
'userid' => $cmt->getUserfk(),
Hope this help

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.

Categories