Map URL with Method in Tonic? - php

I want to map the URL with tonic, how can I do that? is there any particular doc where I can? except for its API doc, because I have read it's API there are no any docs about that?
for example here is my class
/**
* Display and process a HTML form via a HTTP POST request
*
* This page outputs a simple HTML form and gathers the POSTed data
*
*
*
*
* #uri /example/getExample/:name
* #uri /example/getExample/
*
* #uri /Example/:name
*
*/
class example extend Resource{
/**
*
* Handle a GET request for this resource
* #method GET
* #param Request $name
* #return str
*/
function getExamplemethod1($name=null) {
return json_encode(array('status'=>'done method 2'));
}
/**
*
* Handle a POST request for this resource
* #method GET
* #param Request $name
* #return str
*/
function getExamplemethod2($name=null) {
if($name == null){
return json_encode(array('status'=>'done1'));
}
else{
return json_encode(array('status'=>'done1', 'name'=>$name));
}
}
}
I want to call the method in the URL how can I do that?

Related

Solorium issue - syntax error, unexpected ':', expecting ';' or '{' at line number 309 [duplicate]

This question already has answers here:
PHP parse/syntax errors; and how to solve them
(20 answers)
Closed 3 years ago.
I'm setting solarium in my codigniter web application. And create test query. It shows
A PHP Error was encountered
Severity: Warning
Message: Cannot modify header information - headers already sent by (output started at C:\wamp\www\solarium\application\vendor\solarium\solarium\src\Core\Client\Request.php:309)
Filename: core/Common.php
Line Number: 570
Backtrace:
And
A PHP Error was encountered
Severity: Parsing Error
Message: syntax error, unexpected ':', expecting ';' or '{'
Filename: Client/Request.php
Line Number: 309
Backtrace:
I enclose my file in below
<?php
namespace Solarium\Core\Client;
use Solarium\Component\RequestBuilder\RequestParamsInterface;
use Solarium\Component\RequestBuilder\RequestParamsTrait;
use Solarium\Core\Configurable;
use Solarium\Exception\RuntimeException;
/**
* Class for describing a request.
*/
class Request extends Configurable implements RequestParamsInterface
{
use RequestParamsTrait;
/**
* Request GET method.
*/
const METHOD_GET = 'GET';
/**
* Request POST method.
*/
const METHOD_POST = 'POST';
/**
* Request HEAD method.
*/
const METHOD_HEAD = 'HEAD';
/**
* Request DELETE method.
*/
const METHOD_DELETE = 'DELETE';
/**
* Request PUT method.
*/
const METHOD_PUT = 'PUT';
/**
* Default options.
*
* #var array
*/
protected $options = [
'method' => self::METHOD_GET,
];
/**
* Request headers.
*/
protected $headers = [];
/**
* Raw POST data.
*
* #var string
*/
protected $rawData;
/**
* Magic method enables a object to be transformed to a string.
*
* Get a summary showing significant variables in the object
* note: uri resource is decoded for readability
*
* #return string
*/
public function __toString()
{
$output = __CLASS__.'::__toString'."\n".'method: '.$this->getMethod()."\n".'header: '.print_r($this->getHeaders(), 1).'authentication: '.print_r($this->getAuthentication(), 1).'resource: '.$this->getUri()."\n".'resource urldecoded: '.urldecode($this->getUri())."\n".'raw data: '.$this->getRawData()."\n".'file upload: '.$this->getFileUpload()."\n";
return $output;
}
/**
* Set request handler.
*
* #param string $handler
*
* #return self Provides fluent interface
*/
public function setHandler($handler)
{
$this->setOption('handler', $handler);
return $this;
}
/**
* Get request handler.
*
* #return string
*/
public function getHandler()
{
return $this->getOption('handler');
}
/**
* Set request method.
*
* Use one of the constants as value
*
* #param string $method
*
* #return self Provides fluent interface
*/
public function setMethod($method)
{
$this->setOption('method', $method);
return $this;
}
/**
* Get request method.
*
* #return string
*/
public function getMethod()
{
return $this->getOption('method');
}
/**
* Get raw POST data.
*
* #return string
*/
public function getRawData()
{
return $this->rawData;
}
/**
* Set raw POST data.
*
* This string must be safely encoded.
*
* #param string $data
*
* #return self Provides fluent interface
*/
public function setRawData($data)
{
$this->rawData = $data;
return $this;
}
/**
* Get the file to upload via "multipart/form-data" POST request.
*
* #return string|null
*/
public function getFileUpload()
{
return $this->getOption('file');
}
/**
* Set the file to upload via "multipart/form-data" POST request.
*
*
* #param string $filename Name of file to upload
*
* #throws RuntimeException
*
* #return self
*/
public function setFileUpload($filename)
{
if (!is_file($filename) || !is_readable($filename)) {
throw new RuntimeException("Unable to read file '{$filename}' for upload");
}
$this->setOption('file', $filename);
return $this;
}
/**
* Get all request headers.
*
* #return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Set request headers.
*
* #param array $headers
*
* #return self Provides fluent interface
*/
public function setHeaders($headers)
{
$this->clearHeaders();
$this->addHeaders($headers);
return $this;
}
/**
* Add a request header.
*
* #param string|array $value
*
* #return self Provides fluent interface
*/
public function addHeader($value)
{
$this->headers[] = $value;
return $this;
}
/**
* Add multiple headers to the request.
*
* #param array $headers
*
* #return self Provides fluent interface
*/
public function addHeaders($headers)
{
foreach ($headers as $header) {
$this->addHeader($header);
}
return $this;
}
/**
* Clear all request headers.
*
* #return self Provides fluent interface
*/
public function clearHeaders()
{
$this->headers = [];
return $this;
}
/**
* Get an URI for this request.
*
* #return string
*/
public function getUri()
{
return $this->getHandler().'?'.$this->getQueryString();
}
/**
* Set HTTP basic auth settings.
*
* If one or both values are NULL authentication will be disabled
*
* #param string $username
* #param string $password
*
* #return self Provides fluent interface
*/
public function setAuthentication($username, $password)
{
$this->setOption('username', $username);
$this->setOption('password', $password);
return $this;
}
/**
* Get HTTP basic auth settings.
*
* #return array
*/
public function getAuthentication()
{
return [
'username' => $this->getOption('username'),
'password' => $this->getOption('password'),
];
}
/**
* Execute a request outside of the core context in the global solr context.
*
* #param bool $isServerRequest
*/
public function setIsServerRequest($isServerRequest = false)
{
$this->setOption('isserverrequest', $isServerRequest);
}
/**
* Indicates if a request is core independent and could be executed outside a core context.
* By default a Request is not core independent and must be executed in the context of a core.
*
* #return bool
*/
public function getIsServerRequest(): bool
{
return $this->getOption('isserverrequest') ?? false;
}
/**
* Initialization hook.
*/
protected function init()
{
foreach ($this->options as $name => $value) {
switch ($name) {
case 'rawdata':
$this->setRawData($value);
break;
case 'file':
$this->setFileUpload($value);
break;
case 'param':
$this->setParams($value);
break;
case 'header':
$this->setHeaders($value);
break;
case 'authentication':
if (isset($value['username']) && isset($value['password'])) {
$this->setAuthentication($value['username'], $value['password']);
}
}
}
}
/**
* #return string
*/
public function getHash()
{
return spl_object_hash($this);
}
}
I didn't know how to solve this problem.
With the latter issue, you're running PHP 7.0+ code with a version of PHP that is less than 7.0.
With PHP 7.0 came the support of function type hinting, IE:
public function getIsServerRequest(): bool
This states that the function getIsServerRequest will return a boolean, either true or false.
Make sure that you're running the correct version of PHP.
This may also solve the first issue once you have fixed the latter issue.

api-platform | Is there a way to disable the WriteListener on a specific operation or route?

So because of api-platform.com Unable to generate an IRI for the item of type I tried using a different approach and declare custom operations on my user entity for login, registration and reset (since I stil want custom business logics for them). So the initial set-up of that in api-platform is rather easy. I added the following code to my user entity
* collectionOperations={
* "register"={"route_name"="user_register","normalization_context"={"groups"={"registerRead"}},"denormalization_context"={"groups"={"registerWrite"}}},
* "reset"={"route_name"="user_reset","normalization_context"={"groups"={"resetRead"}},"denormalization_context"={"groups"={"resetWrite"}}},
* "login"={"route_name"="user_login","normalization_context"={"groups"={"loginRead"}},"denormalization_context"={"groups"={"loginWrite"}}},
* "token"={"route_name"="user_token","normalization_context"={"groups"={"tokenRead"}},"denormalization_context"={"groups"={"token"}}}
* },
And then added the appropriate actions to the user controller.
/**
* #Route(
* name="user_login",
* path="api/user/login",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="login",
* "_api_receive"=false
* }
* )
*/
public function loginAction(User $data): User {
///$this->userService->login($data);
return $data;
}
/**
* #Route(
* name="user_register",
* path="api/user/register",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="register",
* "_api_receive"=false
* }
* )
*/
public function registerAction(User $data): User {
///$this->userService->register($data);
return $data;
}
/**
* #Route(
* name="user_reset",
* path="api/user/reset",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="reset",
* "_api_receive"=false
* }
* )
*/
public function resetAction(User $data): User {
//$this->userService->reset($data);
return $data;
}
/**
* #Route(
* name="user_token",
* path="api/user/token",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="token",
* "_api_receive"=false
* }
* )
*/
public function tokenAction(User $data): User {
//$this->userService->reset($data);
return $data;
}
So far al fine, however..... because we are using a post operation here and the user is a doctrine ORM entity the api-platform bundle atomically adds the post to the database. But I don’t want that, I want it to pass the entity on to the controller who then uses a service to do business logics. And determine if and how the post should be processed.
Now I went over the documentation and the problem seems to be that the WriteListener always triggers there were other triggers (e.g. ReadListener, DeserializeListener and ValidateListener) can be disabled trough the _api_receive parameter.
So that leaves the question is there a way to disable the WriteListener on a specific operation or route?
Kind Regards,
Ruben van der Linde
You can return an instance of HttpFoundation's Response instead of $data. Then no listener registered on kernel.view will be called.
But introducing a listener similar to api_receive for the write listener is a good idea. Would you mind opening a Pull Request?
Edit: I've opened a Pull Request to introduce this new flag: https://github.com/api-platform/core/pull/2072

Does PHP have a convention for describing Promise returns in function docblock

I just wrote a function like this
/**
* Send an asynchronous GET request
*
* #param string $url
* #param array $options
*
* #return \React\Promise\ExtendedPromiseInterface
*/
public function getAsync( $url, array $options = [] );
but when making docblock, I realized that #return \React\Promise\ExtendedPromiseInterface is very generic and doesn't really help client understand what returns are to be expected in case of rejection or fulfillment.
Is there some established convention for documenting which values or exception are expected as a result of this function so that the client could chain on this function by looking at the interface only?
For exceptions you can add:
/**
* #throws customException if the bad thing happens
*/
You could add as many of these as you like too. After the #return you can add a type before and a short description of what it is after.
Not having found anything, I ended up making this up
/**
* Send an asynchronous GET request
*
* #param string $url
* #param array $options
*
* #return \React\Promise\ExtendedPromiseInterface
*
* #promise.resolve \MyApp\Interfaces\ResponseInterface
* #promise.reject \MyApp\Exceptions\RequestException
*/
public function getAsync( $url, array $options = [] );

Nelmio Api Doc Bundle: Documentating required Parameters

I am currently working with the NelmioApiDocBundle, with which I am not very familiar yet. The API I am writing has to provide a route to change the password of a specific user. The documentation should state, that for changing the password both the old one and the new one are required. Since I did not found an explanation of the difference between Requirementsand Parameters, I guess the first is used for data from the route and the latter is used for the API call itself.
First attempt of archieving such a documentation was to implement a simple Model, which the JMSSerializerBundle then automatically converts:
class ChangePasswordParam
{
/**
* #Type("string")
* #var string
*/
protected $oldPassword;
/**
* #Type("string")
* #var string
*/
protected $newPassword;
}
The Controller accepts the API call via this action method:
/**
* Changes the password for a specific user.
*
* #Post("/{username}/changepassword")
* #View()
* #ApiDoc(
* description="Changes the password of a User",
* input="FQCN\ChangePasswordParam"
* )
*
* #param string $username
* #param ChangePasswordParam $passwordParam
*
* #return Response
*/
public function changePasswordAction($username, ChangePasswordParam $passwordParam)
{
/* ... */
}
This led to the documentation showing username as Requirement, old_password and new_password as Parameter. To mark those Parameters as required, I added a Symfony Constraint via annotation to the properties:
class ChangePasswordParam
{
/**
* #Type("string")
* #Assert\NotNull()
* #var string
*/
protected $oldPassword;
/**
* #Type("string")
* #Assert\NotNull()
* #var string
*/
protected $newPassword;
}
However, while using these annotations marked the properties as required, it does generate strange output:
Notice the parameters being added twice and in different formats? Adding the #SerializedName("old_password") has no effect. Regarding this ticket, the issue is still not solved.
Another way of accepting data for the action is using a custom form, which indeed marks the properties as required but also generates no proper output. Changing the ChangePasswordParam as custom form:
class ChangePasswordParam extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('old_password', 'text');
$builder->add('new_password', 'text');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName()
{
return 'change_password';
}
}
is resulting in this parameter description:
Those parameters should be named just 'old_password' and 'new_password' and I can't figure out how to archieve this.
Thanks in advance
Your #ApiDoc annotation should include an empty input form name field like below:
* #ApiDoc(
* description="Changes the password of a User",
* input= {
* "class" = "FQCN\ChangePasswordParam",
* "name" = ""
* }
* )
This will remove the form name before the parameters name.

Restler 3 nested resources?

In restler, is is possible to use nested resources? For example, with restler I would do the normal /api/account/123 call to get that specific account. Now I want to get the clients that belong to that account. So I'd want to also call /api/account/123/client/456 for example to get the specific client for the specific account.
You can use manual routing to define such routes. See the following example
use Luracast\Restler\RestException;
class Accounts
{
/**
* Get specific client for the given account
*
* #param int $id account id
* #param int $client_id
*
* #throws RestException 404
*
* #return Client
*
* #url GET accounts/{id}/clients/{client_id}
*/
public function getClient($id, $client_id)
{
$r = Client::where('account_id', '=', $id)->where('id', '=', $client_id)->firstOrFail();
if (empty($r))
throw RestException(404, 'Client is not found associated with the account');
return $r;
}
/**
* Get all clients associated with the given account
*
* #param int $id account id
*
* #return array {#type Client}
*
* #url GET accounts/{id}/clients
*/
public function getClients($id)
{
return Client::where('account_id', '=', $id)->all();
}
}

Categories