Using Validation Callback in Kohana 3.2 ORM model - validation

I'm trying to create a custom function to validate multiple fields in kohana 3.2 orm model.
I know that there's a lot of examples showing how to do this from the controller but I want to set it up in the orm model.
I'm trying to do something like this:
public function rules()
{
return array(
'my_field' => array(
array(array($this, 'my_function')),
),
);
}
public function my_function(Validation $validation, $my_field)
{
//Some calculation and then return error..
$validation->add_error($my_field, 'my_field_not_valid');
}

Dont forget to define params for your custom callback:
public function rules()
{
return array(
'my_field' => array(
array(array($this, 'my_function'), array(':validation', ':field')),
),
);
}
http://kohanaframework.org/3.2/guide/kohana/security/validation#binding-variables
PS. Please paste your error/exception text. Usually it already contains correct answer.

Related

Laravel 5: How to create a router model binding on multiple parameters

So far I know how to create a router model binding on single parameters like so:
// RouteServiceProvider.php
$router->model('subject_slug', 'App\Subject', function($slug) {
return Subject::where('slug', $slug)->firstOrFail();
});
The above can then be used like this:
// routes.php
Route::get('/{subject_slug}', 'MenuController#showSubject');
And in the controller:
public function showSubject(Subject $subject) {
....
}
But sometimes I need to specify multiple parameters in order to get the right model.
For example consider the following route:
Route::get('/{subject_slug}/{topic_slug}/', 'MenuController#showTopic');
and the corresponding controller:
public function showTopic(Subject $subject, Topic $topic) {
....
}
However to get the correct model for Topic I need to know the Subject. For example:
// !!! Invalid laravel code !!!
$router->model('topic_slug', 'App\Topic, function($subject_slug, $topic_slug) {
// ERROR: $subject_slug is obviously not defined!
return Topic::where([
'subject_slug' => $subject_slug,
'slug' => $topic_slug,
])->firstOrFail();
});
How can I make a router model binding for Topic bearing in mind I need to know the Subject parameter before it in order to fetch the correct Topic.
Is there an alternative better way of doing this?
UPDATE
Currently my showTopic method in my controller is like this:
public function showTopic(Subject $subject, $topic_slug) {
$topic = Topic::where([
'subject_slug' => $subject_slug,
'slug' => $topic_slug,
])->firstOrFail();
// ...
}
and I have no router model binding for topic_slug.
This works as expected, but I would like to take advantage of router model bindings!
It turns out the way I was doing it was a bit flawed. I was unnessarily using model bindings when instead it would be better to have used a normal binding like so:
$router->bind('topic_slug', function($slug, Route $route) {
$subject = $route->parameter('subject_slug');
return Topic::where([
'subject_slug' => $subject->slug,
'slug' => $slug,
])->firstOrFail();
});
Also I was using model bindings completely wrong before as the 3rd function should be the "not found behaviour" (not for additional logic)!

One-shot laravel validator

I have a form where someone searches for something. Based on this form, I validate if the input is correct:
$validator = Validator::make(Input::all() , array(
'address' =>'required',
));
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
After this, I want to validate something else (that a result object isn't empty), which is completely unrelated to the search. In other words, it's NOT input from a form.
1) Do I create another validator to validate this? Or
2) Is there a better way to simply check this value and spawn an object that can be returned with "withErrors"?
UPDATE
This isn't working for me:
$validator = Validator::make(
array(
'searches' => sizeof($search)
) ,
array(
'searches' => 'required|min:1'
)
);
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
It's not working because for some reason it's picking up that the "searches" item should only be validated "sometimes"
you have two ways. one is custom validator
or there is a simpler way,
suppose,
private function foo()
{
$data = ''; //retrieved the data error here with whatever call you want to make
return !empty($data) ? true : false;
}
in the controller,
public function bar()
{
if(!$this->foo())
{
$messages = new \Illuminate\Support\MessageBag;
// you should use interface here. i directly made the object call for the sake of simplicity.
$messages->add('custom', 'custom error');
return Redirect::back()->withErrors($messages)->withInput();
}
}
in the view:
#if($errors->has('custom'))
<p>custom error output.</p>
#endif
it is just the outline to give you the idea.

codeigniter datamapper get_rules not called after save()

I'm using Datamapper ORM for CodeIgniter I have rules 'serialized' and get_rules 'unserialized' for a field in my model. This field will store serialized data and when I retrieve back, get_rules will unserialize it.
However, after calling save(), I'm trying to re-access the field, but it still return serialized string, instead of array.
Is there any way to re-call or refresh my object so that the get_rules is called again and the field now return array?
Here's my model:
class User extends DataMapper{
public $validation = array(
'password' => array(
'label' => 'Password',
'rules' => array('encrypt')
),
'preferences' => array(
'rules' => array('serialize'),
'get_rules'=> array('unserialize')
)
);
function __construct($id = NULL)
{
parent::__construct($id);
}
function post_model_init($from_cache = FALSE)
{
}
public function _encrypt($field)
{
if (!empty($this->{$field}))
{
$this->{$field} = md5($this->{$field});
}
}
}
Datamapper ORM, afaik, will only use the get_rules when actually performing a get(). A few things you could try:
Given the following
$a = new Fruit();
$a->name = 'grapes';
$a->colors = serialize(array("purple","green"));
$a->save();
1. Create a new datamapper object and re-fetch:
$b = new Fruit();
$b->where('id', $a->id)->get();
$colors = $b->colors;
2. unserialize() the field yourself...
$colors = unserialize($a->colors);
3. You might even be able to use get_clone()
//not tested...
$b = $a->get_clone();
$colors = $b->colors;
This has been fixed here: https://bitbucket.org/wanwizard/datamapper/commits/db6ad5f2e10650b0c00c8ef9b7176d49a8e85163
Get the latest Datamapper library from bitbucket.

How to add an autocomplete field in a Symfony2 form for a collection and using Propel?

I'm using Symfony 2.1 forms with PropelBundle and I'm trying to refactor a form that had a drop-down list of objects (to select from) to instead use a jquery autocomplete field (working with AJAX). For the dropdown list I was using the following code (which worked perfectly for the drop-down) in my form type:
$builder->add('books', 'collection', array(
'type' => 'model',
'options' => array(
'class' => 'MyVendor\MyBundle\Model\Book',
'property' => 'title',
),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
));
For the sake of giving a little context, let's say we are creating a new "Reader" object and that we would like to select the Reader's favorite books from a list of available "Book" objects. A collection type is used so that many "favorite books" can be selected in the new "Reader" form. Now, I would like to change the above to use autocomplete. For doing so, I tried to implement a Data Transformer to be able to get a Book object from a simple text field that could be used for the Autocomplete function to pass the Book ID as indicated in the answer to this Question. However, I was not able to figure out how to make the Data Transformer work with a collection type and Propel classes. I created a BookToIdTransformer class as indicated in the Symfony Cookbook and tried the following in the "ReaderType" file:
$transformer = new BookToIdTransformer();
$builder->add(
$builder->create('books', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
))->addModelTransformer($transformer)
);
With the above, I get a "Call to undefined method: getId" exception (apparently the Transformer expects a PropelCollection of Books, not a single Book object..). Does anyone know how to go about it? or let me know if there are other ways to implement the autocomplete in Symfony using Propel and allowing for selecting multiple objects (e.g. a collection of books)?
The solution I ultimately went for is slightly different from my previous answer. I ended up using a "text" field type instead of a "collection" field type in my "ReaderType" form, since I ended up using the Loopj Tokeninput jQuery plugin which allows for selecting multiple objects (e.g. "Favorite Book") to associate to my main object (e.g. "Reader" object) in the form. Considering that, I created a "Data Transformer" for transforming the objects' ids passed (in a comma separated list in the text field) into a Propel Object Collection. The code is shared as follows, including a sample ajax object controller.
The key part of the "ReaderType" form looks as follows:
$transformer = new BooksToIdsTransformer();
$builder->add(
$builder->create('books', 'text', array(
'required' => false,
))->addModelTransformer($transformer)
);
The "Data Transformer" file looks like this:
// src/MyVendor/MyBundle/Form/DataTransformer/BooksToIdsTransformer.php
namespace MyVendor\MyBundle\Form\DataTransformer;
use \PropelObjectCollection;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use MyVendor\MyBundle\Model\BookQuery;
class BooksToIdsTransformer implements DataTransformerInterface
{
public function transform($books)
{
if (null === $books) {
return "";
}
if (!$books instanceof PropelObjectCollection) {
throw new UnexpectedTypeException($books, '\PropelObjectCollection');
}
$idsArray = array();
foreach ($books as $book) {
$idsArray[] = $book->getId();
}
$ids = implode(",", $idsArray);
return $ids;
}
public function reverseTransform($ids)
{
$books = new PropelObjectCollection();
if ('' === $ids || null === $ids) {
return $books;
}
if (!is_string($ids)) {
throw new UnexpectedTypeException($ids, 'string');
}
$idsArray = explode(",", $ids);
$idsArray = array_filter ($idsArray, 'is_numeric');
foreach ($idsArray as $id) {
$books->append(BookQuery::create()->findOneById($id));
}
return $books;
}
}
The ajax controller that returns a json collection of "books" to the Tokeninput autocomplete function is as follows:
namespace MyVendor\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use MyVendor\MyBundle\Model\BookQuery;
class ClassAjaxController extends Controller
{
public function bookAction(Request $request)
{
$value = $request->get('q');
$books = BookQuery::create()->findByName('%'.$value.'%');
$json = array();
foreach ($books as $book) {
$json[] = array(
'id' => $book->getId(),
'name' => $book->getName()
);
}
$response = new Response();
$response->setContent(json_encode($json));
return $response;
}
}
And finally, the router in the "routing.yml" file:
ajax_book:
pattern: /ajax_book
defaults: { _controller: MySiteBundle:ClassAjax:book }

Disable symfony 2 csrf token protection on ajax submit

i'm building a mobile app talking to my symfony2 app via webservices
I can't find a way to disable csrf protection on a specific controller/action
i want to post registration data to this action and use sf2 form validation. I do not call the form in my mobile app
Can't change container parameters in action, throw an exception because it is a frozen parameter...
I do not want to disable form protection for whole my application
any clue ?
thanks !
update: with symfony 2.1.x
/**
* {#inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false,
));
}
If you're looking for a bit easier and faster solution than suggested in answer above, here's how:
<?php
// ...
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MyType extends AbstractType
{
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false,
));
}
}
.. or if you're using older versions (Symfony 2.0.*):
<?php
// ...
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class MyType extends AbstractType
{
// ....
public function getDefaultOptions(array $options)
{
$options = parent::getDefaultOptions($options);
$options['csrf_protection'] = false;
return $options;
}
}
Consult the Symfony documentation for additional information.
Edit: updated answer to latest Symfony version, thanks
naitsirch
Using the form factory
For those who want to create a simple form in a controller:
$form = $this->container->get('form.factory')
->createNamedBuilder(null, 'form', null, array('csrf_protection' => false))
->add('yourField','text', array(
'label' => false,
'mapped' => false
))
->getForm();
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
Using the form factory in Symfony 3
use Symfony\Component\Form\Extension\Core\Type\FormType;
$form = $this->container->get('form.factory')
->createNamedBuilder(null, FormType::class, null, array('csrf_protection' => false))
->add('yourField','text', array(
'label' => false,
'mapped' => false
))
->getForm();
Adapted from Mick's answer
I can't be 100% sure but I think I read somewhere that you can pass csrf_provider option while creating form.
All providers are subtypes of interface Symfony\Component\Form\Extension\Csrf\CsrfProvider and you should be able to create your own:
class MyNonCsrfProvider extends DefaultCsrfProvider{
public function isCsrfTokenValid($intention, $token)
{
return true;
}
}
and in controller:
$this->createForm(new CustomFormType(), array(
'csrf_provider' => new MyNonCsrfProvider()
));
I haven't tried this myself but this sounds like a possible solution...

Resources