Related
I want to check if a form input 'departement' is filled only if two 'villes' have the same name.
within controller this code wokds perfectly :
$rules=[ 'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
$messages = [
'depart.required' => 'Deux villes portent le même nom, preciser le
département'];
$validator = Validator::make($request->All(), $rules,$messages);
$validator->sometimes('depart_id', 'required|exists:departs,id', function
($input) {
return Ville::where('nom',$input->ville)->count()>1;
});
if ($validator->fails()) {
return redirect('admin/etab/create')
->withErrors($validator)
->withInput();
}
I put the same code in a Form Request class:
public function rules()
{
$rules=[ 'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
$messages = [
'depart.required' => 'Deux villes portent le même nom, preciser le
département',
];
$validator = Validator::make($this->All(), $rules,$messages);
$validator->sometimes('depart_id', 'required|exists:departs,id', function
($input) {
return Ville::where('nom',$input->ville)->count()>1;
});
return $validator;
}
I get "Type error: Argument 2 passed to Illuminate\Validation\Factory::make() must be of the type array, object given," I think error message is inadequate but I cannot find why this way does not work
Thanks ......
You can check out the FormRequest class in vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php and check what it does.
It contains these 2 method at the top:
/**
* Get the validator instance for the request.
*
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function getValidatorInstance()
{
$factory = $this->container->make(ValidationFactory::class);
if (method_exists($this, 'validator')) {
$validator = $this->container->call([$this, 'validator'], compact('factory'));
} else {
$validator = $this->createDefaultValidator($factory);
}
if (method_exists($this, 'withValidator')) {
$this->withValidator($validator);
}
return $validator;
}
/**
* Create the default validator instance.
*
* #param \Illuminate\Contracts\Validation\Factory $factory
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function createDefaultValidator(ValidationFactory $factory)
{
return $factory->make(
$this->validationData(), $this->container->call([$this, 'rules']),
$this->messages(), $this->attributes()
);
}
So you can basically provide a validator method in your own FormRequest class to create a custom Validator object, that method will get the ValidatorFactory as param.
In your case you would not need to do this, because you just want to append the sometimes rule to a default validator. Looking at the code above, it checks for the existence of the withValidator method, if it exists, it is called:
if (method_exists($this, 'withValidator')) {
$this->withValidator($validator);
}
You could create the FormRequest, make sure the rules, messages and authorize methods are properly used, e.g. rules and messages return arrays and authorize returns a bool.
Then create a withValidator method in which you attach the sometimes rule to the Validator.
/**
* Do foo with Validator
*
* #param \Illuminate\Contracts\Validation\Validator $validator
* #return void
*/
public function withValidator(Validator $validator)
{
$validator->sometimes('depart_id', 'required|exists:departs,id', function {
return Ville::where('nom', $this->input('ville'))->count() > 1;
});
}
This way sometimes is attached to your validator before the validation is performed.
You don't put all the validation logic in the rules method like that. Only the rule definitions go there. All you need is this:
public function rules()
{
return [
'nom' => 'required',
'ville'=> 'required|exists:villes,nom',
];
}
Laravel will handle the validation from there on out. You don't need to manually create a Validator class when using FormRequests.
Customizing the message involves creating a messages method within the class like so:
public function messages()
{
return [
'depart.required' => 'Deux villes portent le même nom, preciser le départemen'
];
}
Update
As for the sometimes rule, I'd suggest creating a Rule object and customizing how you need to check for 2 vills having the same name.
Rule classes
Hi need some help with uniting testing a Symfony 2.8 callback.
I don't think I have set it up correctly as the test is passing when I know it should be failing
The entity setup:
The validate callback in the Contact entity:
/**
* Validation callback for contact
* #param \AppBundle\Entity\Contact $object
* #param ExecutionContextInterface $context
*/
public static function validate(Contact $object, ExecutionContextInterface $context)
{
/**
* Check if the country code is valid
*/
if ($object->isValidCountryCode() === false) {
$context->buildViolation('Cannot register in that country')
->atPath('country')
->addViolation();
}
}
The method isValidCountryCode in Contact entity:
/**
* Get a list of invalid country codes
* #return array Collection of invalid country codes
*/
public function getInvalidCountryCodes()
{
return array('IS');
}
The method that checks if the country code is invalid:
/**
* Check if the country code is valid
* #return boolean
*/
public function isValidCountryCode()
{
$invalidCountryCodes = $this->getInvalidCountryCodes();
if (in_array($this->getCountry()->getCode(), $invalidCountryCodes)) {
return false;
}
return true;
}
The validation.yml
AppBundle\Entity\Contact:
properties:
//...
country:
//..
- Callback:
callback: [ AppBundle\Entity\Contact, validate ]
groups: [ "AppBundle" ]
The test class:
//..
use Symfony\Component\Validator\Validation;
class CountryTest extends WebTestCase
{
//...
public function testValidate()
{
$country = new Country();
$country->setCode('IS');
$contact = new Contact();
$contact->setCountry($country);
$validator = Validation::createValidatorBuilder()->getValidator();
$errors = $validator->validate($contact);
$this->assertEquals(1, count($errors));
}
This test returns $errors with a count of 0 but it should be 1 as the country code 'IS' is invalid.
First problem is about the definition of the constraint in the yml files: you need to put the callback under the constraint section instead of properties, so change the validation.yml files as follow:
validation.yml
AppBundle\Entity\Contact:
constraints:
- Callback:
callback: [ AppBundle\Entity\Contact, validate ]
groups: [ "AppBundle" ]
Second in the testCase: you need to take the validator service from the container instead of create a new one with the builder: this object is not initializated with the object structure ect.
Third The callback constraints is defined for the AppBundle validation group only, so pass the validation group to the validator service (As third argument of the service).
So change the testClass as follow:
public function testValidate()
{
$country = new Country();
$country->setCode('IS');
$contact = new Contact();
$contact->setCountry($country);
// $validator = Validation::createValidatorBuilder()->getValidator();
$validator = $this->createClient()->getContainer()->get('validator');
$errors = $validator->validate($contact, null, ['AppBundle']);
$this->assertEquals(1, count($errors));
}
And the testcase became green.
Hope this help
I want to validate the route parameters in the "form request" but don't know how to do it.
Below is the code sample, I am trying with:
Route
// controller Server
Route::group(['prefix' => 'server'], function(){
Route::get('checkToken/{token}',['as'=>'checkKey','uses'=> 'ServerController#checkToken']);
});
Controller
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests;
class ServerController extends Controller {
public function checkToken( \App\Http\Requests\CheckTokenServerRequest $request) // OT: - why I have to set full path to work??
{
$token = Token::where('token', '=', $request->token)->first();
$dt = new DateTime;
$token->executed_at = $dt->format('m-d-y H:i:s');
$token->save();
return response()->json(json_decode($token->json),200);
}
}
CheckTokenServerRequest
namespace App\Http\Requests;
use App\Http\Requests\Request;
class CheckTokenServerRequest extends Request {
//autorization
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
}
}
But when I try to validate a simple url http://myurl/server/checkToken/222, I am getting the response: no " token " parameter set.
Is it possible to validate the parameters in a separate "Form request", Or I have to do all in a controller?
ps. Sorry for my bad English.
For Laravel < 5.5:
The way for this is overriding all() method for CheckTokenServerRequest like so:
public function all()
{
$data = parent::all();
$data['token'] = $this->route('token');
return $data;
}
EDIT
For Laravel >= 5.5:
Above solution works in Laravel < 5.5. If you want to use it in Laravel 5.5 or above, you should use:
public function all($keys = null)
{
$data = parent::all($keys);
$data['token'] = $this->route('token');
return $data;
}
instead.
Override the all() function on the Request object to automatically apply validation rules to the URL parameters
class SetEmailRequest
{
public function rules()
{
return [
'email' => 'required|email|max:40',
'id' => 'required|integer', // << url parameter
];
}
public function all()
{
$data = parent::all();
$data['id'] = $this->route('id');
return $data;
}
public function authorize()
{
return true;
}
}
Access the data normally from the controller like this, after injecting the request:
$setEmailRequest->email // request data
$setEmailRequest->id, // url data
If you dont want to specify each route param and just put all route params you can override like this:
Laravel < 5.5:
public function all()
{
return array_merge(parent::all(), $this->route()->parameters());
}
Laravel 5.5 or above:
public function all($keys = null)
{
// Add route parameters to validation data
return array_merge(parent::all(), $this->route()->parameters());
}
The form request validators are used for validating HTML form data that are sent to server via POST method. It is better that you do not use them for validating route parameters. route parameters are mostly used for retrieving data from data base so in order to ensure that your token route parameter is correct change this line of your code, from
$token = Token::where('token', '=', $request->token)->first();
to
$token = Token::where('token', '=', $request->input(token))->firstOrFail();
firstOrFail() is a very good function, it sends 404 to your user, if the user insert any invalid token.
you get no " token " parameter set because Laravel assumes that your "token" parameter is a POST data which in your case it is not.
if you insist on validating your "token" parameter, by form request validators you gonna slow down your application, because you perform two queries to your db,
one in here
$token = Token::where('token', '=', $request->token)->first();
and one in here
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
I suggest to use firsOrFail to do both validating and retrieving at once.
A trait can cause this validation to be relatively automagic.
Trait
<?php
namespace App\Http\Requests;
/**
* Class RouteParameterValidation
* #package App\Http\Requests
*/
trait RouteParameterValidation{
/**
* #var bool
*/
private $captured_route_vars = false;
/**
* #return mixed
*/
public function all(){
return $this->capture_route_vars(parent::all());
}
/**
* #param $inputs
*
* #return mixed
*/
private function capture_route_vars($inputs){
if($this->captured_route_vars){
return $inputs;
}
$inputs += $this->route()->parameters();
$inputs = self::numbers($inputs);
$this->replace($inputs);
$this->captured_route_vars = true;
return $inputs;
}
/**
* #param $inputs
*
* #return mixed
*/
private static function numbers($inputs){
foreach($inputs as $k => $input){
if(is_numeric($input) and !is_infinite($inputs[$k] * 1)){
$inputs[$k] *= 1;
}
}
return $inputs;
}
}
Usage
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyCustomRequest extends FormRequest{
use RouteParameterValidation;
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize(){
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules(){
return [
//
'any_route_param' => 'required'//any rule(s) or custom rule(s)
];
}
}
For \App\Http\Requests\CheckTokenServerRequest you can add use App\Http\Requests\CheckTokenServerRequest; at the top.
If you pass the token by url you can use it likes a variable in controller.
public function checkToken($token) //same with the name in url
{
$_token = Token::where('token', '=', $token)->first();
$dt = new DateTime;
$_token->executed_at = $dt->format('m-d-y H:i:s');
$_token->save();
return response()->json(json_decode($token->json),200);
}
$request->merge(['id' => $id]);
...
$this->validate($request, $rules);
or
$request->merge(['param' => $this->route('param')]);
...
$this->validate($request, $rules);
You just missing the underscore before token. Replace with
_token
wherever you check it against the form generated by laravel.
public function rules()
{
return [
'_token' => ['required','exists:Tokens,token,executed_at,null']
];
FormRequest has a method validationData() that defines what data to use for validation. So just override that one with route parameters in your form request class:
/**
* Use route parameters for validation
* #return array
*/
protected function validationData()
{
return $this->route()->parameters();
}
or leave most of the all logic in place and override input method from trait \Illuminate\Http\Concerns\InteractsWithInput
/**
* Retrieve an input item from the request.
*
* #param string|null $key
* #param string|array|null $default
* #return string|array|null
*/
public function input($key = null, $default = null)
{
return data_get(
$this->getInputSource()->all() + $this->query->all() + $this->route()->parameters(), $key, $default
);
}
I'm developing game app and using Symfony 2.0. I have many AJAX requests to the backend. And more responses is converting entity to JSON. For example:
class DefaultController extends Controller
{
public function launchAction()
{
$user = $this->getDoctrine()
->getRepository('UserBundle:User')
->find($id);
// encode user to json format
$userDataAsJson = $this->encodeUserDataToJson($user);
return array(
'userDataAsJson' => $userDataAsJson
);
}
private function encodeUserDataToJson(User $user)
{
$userData = array(
'id' => $user->getId(),
'profile' => array(
'nickname' => $user->getProfile()->getNickname()
)
);
$jsonEncoder = new JsonEncoder();
return $jsonEncoder->encode($userData, $format = 'json');
}
}
And all my controllers do the same thing: get an entity and encode some of its fields to JSON. I know that I can use normalizers and encode all entitities. But what if an entity has cycled links to other entity? Or the entities graph is very big? Do you have any suggestions?
I think about some encoding schema for entities... or using NormalizableInterface to avoid cycling..,
With php5.4 now you can do :
use JsonSerializable;
/**
* #Entity(repositoryClass="App\Entity\User")
* #Table(name="user")
*/
class MyUserEntity implements JsonSerializable
{
/** #Column(length=50) */
private $name;
/** #Column(length=50) */
private $login;
public function jsonSerialize()
{
return array(
'name' => $this->name,
'login'=> $this->login,
);
}
}
And then call
json_encode(MyUserEntity);
Another option is to use the JMSSerializerBundle. In your controller you then do
$serializer = $this->container->get('serializer');
$reports = $serializer->serialize($doctrineobject, 'json');
return new Response($reports); // should be $reports as $doctrineobject is not serialized
You can configure how the serialization is done by using annotations in the entity class. See the documentation in the link above. For example, here's how you would exclude linked entities:
/**
* Iddp\RorBundle\Entity\Report
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Iddp\RorBundle\Entity\ReportRepository")
* #ExclusionPolicy("None")
*/
....
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="reports")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
* #Exclude
*/
protected $client;
You can automatically encode into Json, your complex entity with:
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');
To complete the answer: Symfony2 comes with a wrapper around json_encode:
Symfony/Component/HttpFoundation/JsonResponse
Typical usage in your Controllers:
...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}
I found the solution to the problem of serializing entities was as follows:
#config/config.yml
services:
serializer.method:
class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
serializer.encoder.json:
class: Symfony\Component\Serializer\Encoder\JsonEncoder
serializer:
class: Symfony\Component\Serializer\Serializer
arguments:
- [#serializer.method]
- {json: #serializer.encoder.json }
in my controller:
$serializer = $this->get('serializer');
$entity = $this->get('doctrine')
->getRepository('myBundle:Entity')
->findOneBy($params);
$collection = $this->get('doctrine')
->getRepository('myBundle:Entity')
->findBy($params);
$toEncode = array(
'response' => array(
'entity' => $serializer->normalize($entity),
'entities' => $serializer->normalize($collection)
),
);
return new Response(json_encode($toEncode));
other example:
$serializer = $this->get('serializer');
$collection = $this->get('doctrine')
->getRepository('myBundle:Entity')
->findBy($params);
$json = $serializer->serialize($collection, 'json');
return new Response($json);
you can even configure it to deserialize arrays in http://api.symfony.com/2.0
I just had to solve the same problem: json-encoding an entity ("User") having a One-To-Many Bidirectional Association to another Entity ("Location").
I tried several things and I think now I found the best acceptable solution. The idea was to use the same code as written by David, but somehow intercept the infinite recursion by telling the Normalizer to stop at some point.
I did not want to implement a custom normalizer, as this GetSetMethodNormalizer is a nice approach in my opinion (based on reflection etc.). So I've decided to subclass it, which is not trivial at first sight, because the method to say if to include a property (isGetMethod) is private.
But, one could override the normalize method, so I intercepted at this point, by simply unsetting the property that references "Location" - so the inifinite loop is interrupted.
In code it looks like this:
class GetSetMethodNormalizer extends \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer {
public function normalize($object, $format = null)
{
// if the object is a User, unset location for normalization, without touching the original object
if($object instanceof \Leonex\MoveBundle\Entity\User) {
$object = clone $object;
$object->setLocations(new \Doctrine\Common\Collections\ArrayCollection());
}
return parent::normalize($object, $format);
}
}
I had the same problem and I chosed to create my own encoder, which will cope by themself with recursion.
I created classes which implements Symfony\Component\Serializer\Normalizer\NormalizerInterface, and a service which holds every NormalizerInterface.
#This is the NormalizerService
class NormalizerService
{
//normalizer are stored in private properties
private $entityOneNormalizer;
private $entityTwoNormalizer;
public function getEntityOneNormalizer()
{
//Normalizer are created only if needed
if ($this->entityOneNormalizer == null)
$this->entityOneNormalizer = new EntityOneNormalizer($this); //every normalizer keep a reference to this service
return $this->entityOneNormalizer;
}
//create a function for each normalizer
//the serializer service will also serialize the entities
//(i found it easier, but you don't really need it)
public function serialize($objects, $format)
{
$serializer = new Serializer(
array(
$this->getEntityOneNormalizer(),
$this->getEntityTwoNormalizer()
),
array($format => $encoder) );
return $serializer->serialize($response, $format);
}
An example of a Normalizer :
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class PlaceNormalizer implements NormalizerInterface {
private $normalizerService;
public function __construct($normalizerService)
{
$this->service = normalizerService;
}
public function normalize($object, $format = null) {
$entityTwo = $object->getEntityTwo();
$entityTwoNormalizer = $this->service->getEntityTwoNormalizer();
return array(
'param' => object->getParam(),
//repeat for every parameter
//!!!! this is where the entityOneNormalizer dealt with recursivity
'entityTwo' => $entityTwoNormalizer->normalize($entityTwo, $format.'_without_any_entity_one') //the 'format' parameter is adapted for ignoring entity one - this may be done with different ways (a specific method, etc.)
);
}
}
In a controller :
$normalizerService = $this->get('normalizer.service'); //you will have to configure services.yml
$json = $normalizerService->serialize($myobject, 'json');
return new Response($json);
The complete code is here : https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer
in Symfony 2.3
/app/config/config.yml
framework:
# сервис конвертирования объектов в массивы, json, xml и обратно
serializer:
enabled: true
services:
object_normalizer:
class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
tags:
# помечаем к чему относится этот сервис, это оч. важно, т.к. иначе работать не будет
- { name: serializer.normalizer }
and example for your controller:
/**
* Поиск сущности по ИД объекта и ИД языка
* #Route("/search/", name="orgunitSearch")
*/
public function orgunitSearchAction()
{
$array = $this->get('request')->query->all();
$entity = $this->getDoctrine()
->getRepository('IntranetOrgunitBundle:Orgunit')
->findOneBy($array);
$serializer = $this->get('serializer');
//$json = $serializer->serialize($entity, 'json');
$array = $serializer->normalize($entity);
return new JsonResponse( $array );
}
but the problems with the field type \DateTime will remain.
This is more an update (for Symfony v:2.7+ and JmsSerializer v:0.13.*#dev), so to avoid that Jms tries to load and serialise the whole object graph ( or in case of cyclic relation ..)
Model:
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Exclude;
use JMS\Serializer\Annotation\MaxDepth; /* <=== Required */
/**
* User
*
* #ORM\Table(name="user_table")
///////////////// OTHER Doctrine proprieties //////////////
*/
public class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="FooBundle\Entity\Game")
* #ORM\JoinColumn(nullable=false)
* #MaxDepth(1)
*/
protected $game;
/*
Other proprieties ....and Getters ans setters
......................
......................
*/
Inside an Action:
use JMS\Serializer\SerializationContext;
/* Necessary include to enbale max depth */
$users = $this
->getDoctrine()
->getManager()
->getRepository("FooBundle:User")
->findAll();
$serializer = $this->container->get('jms_serializer');
$jsonContent = $serializer
->serialize(
$users,
'json',
SerializationContext::create()
->enableMaxDepthChecks()
);
return new Response($jsonContent);
If you are using Symfony 2.7 or above, and don't want to include any additional bundle for serializing, maybe you can follow this way to seialize doctrine entities to json -
In my (common, parent) controller, I have a function that prepares the serializer
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
// -----------------------------
/**
* #return Serializer
*/
protected function _getSerializer()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
return new Serializer([$normalizer], [new JsonEncoder()]);
}
Then use it to serialize Entities to JSON
$this->_getSerializer()->normalize($anEntity, 'json');
$this->_getSerializer()->normalize($arrayOfEntities, 'json');
Done!
But you may need some fine tuning. For example -
If your entities have circular reference, check how to handle it.
If you want to ignore some properties, can do it
Even better, you can serialize only selective attributes.
When you need to create a lot of REST API endpoints on Symfony,
the best way is to use the following stack of bundles:
JMSSerializerBundle for the serialization of Doctrine entities
FOSRestBundle bundle for response view listener. Also, it can generate definitions of routes based on controller/action name.
NelmioApiDocBundle to auto-generate online documentation and Sandbox(which allows testing endpoint without any external tool).
When you configure everything properly, you entity code will look like this:
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Table(name="company")
*/
class Company
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*
* #JMS\Expose()
* #JMS\SerializedName("name")
* #JMS\Groups({"company_overview"})
*/
private $name;
/**
* #var Campaign[]
*
* #ORM\OneToMany(targetEntity="Campaign", mappedBy="company")
*
* #JMS\Expose()
* #JMS\SerializedName("campaigns")
* #JMS\Groups({"campaign_overview"})
*/
private $campaigns;
}
Then, code in controller:
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\View;
class CompanyController extends Controller
{
/**
* Retrieve all companies
*
* #View(serializerGroups={"company_overview"})
* #ApiDoc()
*
* #return Company[]
*/
public function cgetAction()
{
return $this->getDoctrine()->getRepository(Company::class)->findAll();
}
}
The benefits of such a set up are:
#JMS\Expose() annotations in the entity can be added to simple fields, and to any type of relations. Also, there is the possibility to expose the result of some method execution (use annotation #JMS\VirtualProperty() for that)
With serialization groups, we can control exposed fields in different situations.
Controllers are very simple. The action method can directly return an entity or array of entities, and they will be automatically serialized.
And #ApiDoc() allows testing the endpoint directly from the browser, without any REST client or JavaScript code
Now you can also use Doctrine ORM Transformations to convert entities to nested arrays of scalars and back
The accepted answer is correct but if You'll need to serialize a filtered subset of an Entity , json_encode is enough:
Consider this example:
class FileTypeRepository extends ServiceEntityRepository
{
const ALIAS = 'ft';
const SHORT_LIST = 'ft.name name';
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, FileType::class);
}
public function getAllJsonFileTypes()
{
return json_encode($this->getAllFileTypes());
}
/**
* #return array
*/
public function getAllFileTypes()
{
$query = $this->createQueryBuilder(self::ALIAS);
$query->select(self::SHORT_LIST);
return $query->getQuery()->getResult();
}
}
/** THIS IS ENOUGH TO SERIALIZE AN ARRAY OF ENTITIES SINCE the doctrine SELECT will remove complex data structures from the entities itself **/
json_encode($this->getAllFileTypes());
Short note: Tested at least on Symfony 5.1
I want to ensure that a certain text field does not contain a specific value. Is there any way for me to do this using CI's Form Validation class or do I have to write my own extension for it?
I would extend the form validation class:
http://codeigniter.com/user_guide/general/creating_libraries.html
Something like
<?
class MY_Form_validation extends CI_Form_validation {
function __constuct() {
parent::__constuct();
}
function isnt($str,$field){
$this->CI->form_validation->set_message('isnt', "%s contains an invalid response");
return $str!==$field;
}
}
?>
Your validation rule would be something like
trim|alpha_numeric|isnt[invalid value]
Or, you can create a callback function instead of extending the class. The form validation section of the CI user guide has a relevant example:
http://codeigniter.com/user_guide/libraries/form_validation.html#callbacks
I agree with Billiam that you should extend the Form_validation class
I find it is more likely that one would want to validate a white-list of possible string values rather than a black-list. For example, you know your 'shirt_size' field should only return the string values: 'xl','l','m','s'. My solution is to handle both cases.
I use these methods in MY_From_validation:
/**
* ENUM
* The submitted string must match one of the values given
*
* usage:
* enum[value_1, value_2, value_n]
*
* example (any value beside exactly 'ASC' or 'DESC' are invalid):
* $rule['order_by'] = "required|enum[ASC,DESC]";
*
* example of case-insenstive enum using strtolower as validation rule
* $rule['favorite_corey'] = "required|strtolower|enum[feldman]";
*
* #access public
* #param string $str the input to validate
* #param string $val a comma separated lists of values
* #return bool
*/
function enum($str, $val='')
{
if (empty($val))
{
return FALSE;
}
$arr = explode(',', $val);
$array = array();
foreach($arr as $value)
{
$array[] = trim($value);
}
return (in_array(trim($str), $array)) ? TRUE : FALSE;
}
// --------------------------------------------------------------------
/**
* NOT ENUM
* The submitted string must NOT match one of the values given
*
* usage:
* enum[value_1, value_2, value_n]
*
* example (any input beside exactly 'feldman' or 'haim' are valid):
* $rule['favorite_corey'] = "required|not_enum['feldman','haim']";
*
* #access public
* #param string $str the input to validate
* #param string $val a comma separated lists of values
* #return bool
*/
function not_enum($str, $val='')
{
return ($this->enum($str,$val) === TRUE)? FALSE : TRUE;
}
Using Billiam's example, the validation rule not allow string 'invalid value' would be something like:
trim|alpha_numeric|not_enum[invalid value]
Actually, there's a fairly simple example given for this very question in the User Guide - for V2 or V3
Look for the section "Callbacks: Your own Validation Functions". In the example it uses a check for the word "test" in the username field, and returns the custom error if the value is found.
In your controller, change the "username" rule to this:
$this->form_validation->set_rules('username', 'Username', 'callback_username_check');
Then add a new function called username_check to your controller:
function username_check($str)
{
if ($str == 'test')
{
$this->form_validation->set_message('username_check', 'The %s field can not be the word "test"');
return FALSE;
}
else
{
return TRUE;
}
}
And Bob's your uncle...
CodeIgniter's form validation class can call almost any declared PHP function in your rule set. So I would simply declare a function like so:
class yourController {
function someFunction() {
$this->form_validation->set_rules('the_field_you_want_to_check', 'The Field Name', 'trim|myvalfunc[not this value]|xss');
}
}
function myvalfunc($formvalue, $notallowed) {
$this->CI->form_validation->set_message('myvalfunc', "%s is not allowed");
return $formvalue !== $nowallowed;
}