I have a customer request as follows:
<textarea name="intro[en]"></textarea>
<textarea name="intro[fr]"></textarea>
<textarea name="intro[de]"></textarea>
I am validating it with a custom request:
class UpdateProfileRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'intro.*' => 'required|max:100'
];
}
}
The validator is not working. I think this is because the .* only works for numbered arrays, rather than associative arrays?
I'm not sure how to go about doing this.
Is there a way to do it with a custom request like this? If so what is the syntax?
Otherwise, what should I do. I already wrote some custom code inside the controller method like this:
$hasIntro = false;
$hasBio = false;
foreach($request->get('intro') as $language => $localIntro)
{
if(!empty($request->get('intro')[$language]))
{
$hasIntro = true;
}
}
if(!$hasIntro or !$hasBio)
{
return redirect()->back()->withErrors('You must enter at least 1 Bio and 1 Intro');
}
Which I think might be one manual way of going about this. Though I believe withErrors requires a validator, so I'm back to the same problem... Though perhaps there is a way to do this manually?
My ideal solution is to find the associative array syntax, if that indeed exists?
I'm not sure about the right way
but my idea is something like this
public function rules($inputs)
{
$rules = [];
foreach ($inputs as $key => $val) {
if ( strpos($key, "intro") === 0 ){
$rules[$key] = 'required|max:100';
}
}
return $rules;
}
class UpdateProfileRequest extends Request
{
/**
* 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 [
'intro.*' => 'required|max:100'
];
}
/**
* #param Validator $validator
*
* #return mixed
*/
protected function formatErrors(Validator $validator)
{
return $validator->errors()->all();
}
}
You have below same name so make sure it's different or remove one, change name.
<textarea name="intro[fr]"></textarea>
<textarea name="intro[fr]"></textarea>
public function rules()
{
$rules = [];
$intro = $this->request->get('intro');
if (!empty($intro)) {
foreach ($intro as $index => $doc) {
$rules[sprintf('intro.%d', $index)] = 'required|max:100';
}
}
return $rules;
}
Related
I am not sure, what is going on here. I have a collection of Model ID's but want to fallback on using all if specific ID's are omitted.
So I have this code:
use App\Models\Post;
function list($ids = [])
{
$posts = collect($ids)->whenEmpty(function ($posts) {
return Post::all()->pluck('id');
})->each(function ($item, $key) {
$post = Post::findOrFail($item);
});
}
Works fine if I pass in specific IDs via $ids. But when I leave it blank Post::all()->pluck('id'); inside of whenEmpty() returns empty. But if I call Post::all()->pluck('id'); outside the collection it works just fine. So I thought it might be some sort of scoping issue since its inside a closure, but changing it to:
use App\Models\Post;
function list($ids = [])
{
$posts = collect($ids)->whenEmpty(function ($posts) {
return \App\Models\Post::all()->pluck('id');
})->each(function ($item, $key) {
dd($item);
});
}
Is still showing up as "" If I dd() the whole collection its just:
[
0 => ""
]
So even providing the whole namespace isn't working. What am I missing here?
Here it's one approach more
function list(array $ids = [])
{
if(empty($ids)) $posts = Post::all();
else $posts = collect($ids)->map(function($id) {
return Post::findOrFail($id);
});
$posts->each(...); /* collection */
}
if you want to use whenEmpty()
function list(array $ids = [])
{
collect($ids)->map(function($id) {
return Post::findOrFail($id);
})->whenEmpty(function($posts){
return $posts = Post::all();
})->each(...);
}
I know this might not directly answer your question (because I would do this in a different way) but maybe it's helpful for you or others in the same situation.
What I would do is using a Request class to validate the introduced IDs like this:
use Illuminate\Validation\Rules\Exists;
class PostsRequests extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'ids' => ['array'],
'ids.*' => ['numeric', new Exists('posts','id')],
];
}
/**
* Handle a passed validation attempt.
*
* #return void
*/
public function passedValidation()
{
$this->ids = $this->validated()['ids'];
}
}
This way you make sure all the IDs introduced in the array exist in the posts table. Otherwise, the request fails.
Now that you know all the IDs are valid, you can simply check if the array is empty or not:
class PostController extends Controller
{
public function list(PostsRequests $request)
{
$posts = empty($request->ids) ? Post::all() : Post::whereIn('id', $request->ids])->get();
}
}
UPDATE
Since the array of IDs is not coming from a request, you can use a Validator in the controller itself:
use Illuminate\Validation\Rules\Exists;
use Illuminate\Support\Facades\Validator;
class PostController extends Controller
{
public function list(array $ids)
{
Validator::make($ids, [
'*' => ['numeric', new Exists('posts','id')],
])->validate();
$posts = empty($ids) ? Post::all() : Post::whereIn('id', $ids])->get();
}
}
I am trying to return my validation errors to angular but I can't figure out how to return them in the format array('field under validation' => 'error message'). This exact array is held in errors->messages() but it is a protected property.
here is my code
validator.php
<?php namespace TrainerCompare\Services\Validation;
use Validator as V;
/**
*
*/
abstract class Validator
{
protected $errors;
public function validate($data)
{
$validator = V::make($data, static::$rules);
if ($validator->fails()) {
$this->errors = $validator->messages();
return false;
}
return true;
}
public function errors()
{
return $this->errors;
}
}
controller
<?php
use TrainerCompare\Services\Validation\ProgramValidator;
class ProgramsController extends BaseController
{
protected $program;
protected $validator;
public function __construct(Program $program, ProgramValidator $validator)
{
$this->program = $program;
$this->validator = $validator;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
$input = Input::all();
$v = $this->validator->validate($input);
if ($v == true) {
//$this->program->create($input);
return Response::json(
array('success' => true)
);
} else {
$errors = $this->validator->errors();
return Response::json(
array('errors' => $errors)
);
}
}
}
this returns the json
{"errors":{}}
if I change the controller to
$errors = $this->calidator->errors()->all();
this is returned
{"errors":["The title field is required.","The focus field is required.","The desc field is required."]}
what I really want returned is
{"errors":[title: "The title field is required.",focus: "The focus field is required.",desc: "The desc field is required."]}
The Validator errors in Laravel return a MessageBag object, which has many useful methods you may want to look over.
It sounds like what you want is the toArray method, and you can use it like so in your controller.
Replace the following code that you have in your controller;
$errors = $this->validator->errors();
return Response::json(
array('errors' => $errors)
);
With;
$errors = $this->validator->errors()->toArray();
return Response::json(
array('errors' => $errors)
);
Or, depending how how you're using it with Angular, you can return the object directly, using the toJson method.
return $this->validator->errors()->toJson();
I hope I can explain this clearly, apologies in advance if it is confusing. I have a goals table which hasOne of each of bodyGoalDescs, strengthGoalDescs and distanceGoalDescs as shown below
goals.php
class Goal extends BaseModel
{
protected $guarded = array();
public static $rules = array();
//define relationships
public function user()
{
return $this->belongsTo('User', 'id', 'userId');
}
public function goalStatus()
{
return $this->hasOne('GoalStatus', 'id', 'goalStatus');
}
public function bodyGoalDesc()
{
return $this->hasOne('BodyGoalDesc', 'id', 'bodyGoalId');
}
public function distanceGoalDesc()
{
return $this->hasOne('DistanceGoalDesc', 'id', 'distanceGoalId');
}
public function strengthGoalDesc()
{
return $this->hasOne('StrengthGoalDesc', 'id', 'strengthGoalId');
}
//goal specific functions
public static function yourGoals()
{
return static::where('userId', '=', Auth::user()->id)->paginate();
}
}
each of the three tables looks like this with the function details changed
class BodyGoalDesc extends BaseModel
{
protected $guarded = array();
public static $rules = array();
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'bodyGoalDescs';
//define relationships
public function goal()
{
return $this->belongsTo('Goal', 'bodyGoalId', 'id');
}
}
a goal has either a body goal, a strength goal, or a distance goal. I am having a problem with this method in the controller function
<?php
class GoalsController extends BaseController
{
protected $goal;
public function __construct(Goal $goal)
{
$this->goal = $goal;
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$thisgoal = $this->goal->find($id);
foreach ($this->goal->with('distanceGoalDesc')->get() as $distancegoaldesc) {
dd($distancegoaldesc->DistanceGoalDesc);
}
}
}
when I pass through goal 1 which has a distance goal the above method dies and dumps the Goal object with the details of goal 1 and an array of its relations including an object with DistanceGoalDes.
when I pass through goal 2 it passes through exactly the same as if I had passed through goal 1
if I dd() $thisgoal i get the goal that was passed through
what I want ultimately is a method that returns the goal object with its relevant goal description object to the view but this wont even show me the correct goal details not too mind with the correct relations
this function is now doing what I want it to do, I am sure there is a better way (besides the fact that its happening in the controller right now) and I would love to hear it.
public function show($id)
{
$thisgoal = $this->goal->find($id);
if (!$thisgoal->bodyGoalDesc == null) {
$goaldesc = $thisgoal->bodyGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('bodygoaldesc', $goaldesc);
} elseif (!$thisgoal->strengthGoalDesc == null) {
$goaldesc = $thisgoal->strengthGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('strengthgoaldesc', $goaldesc);
} elseif (!$thisgoal->distanceGoalDesc == null) {
$goaldesc = $thisgoal->distanceGoalDesc;
return View::make('goals.show')
->with('goal', $thisgoal)
->with('distancegoaldesc', $goaldesc);
}
}
I'm trying to include unit-testing in a new Laravel app I'm building.
Right now I'm want to test my OrderController. The index method of this controller looks like this:
public function index()
{
// We need the extra 'orders()' for the query scope
$orders = $this->order->orders()->paginate($this->perPage);
$this->layout->content = View::make('orders.index', compact('orders'));
}
Now I have my test that looks like this:
public function testIndex()
{
$this->call('GET', 'orders');
$this->assertResponseOk();
$this->assertViewHas('orders');
}
Now if I run phpunit, the test does run, but I'm getting: 1) OrderControllerTest::testIndex
Failed asserting that an array has the key 'orders'.
I've tracked down the issue to using Controller Layouts $this->layout.
If I just do return View::make() the test does pass, if I return $this->layout... it also does pass, but this destroys the actual app.
So only option I've found is to use return View::make() and have #extends('master') in that view. But it's seems strange to me that you can't use Controller Layouts in your app if you want to test it.
Am I doing something wrong?
Edit:
I had the same problem and here is slightly messy solution that works!
View::composer('ordersviewname', function($view) {
$this->assertArrayHasKey('orders', $view->getData());
});
$this->call('GET', 'orders');
Note that you have to put this code BEFORE $this->call()
EDIT:
Here is more elegant solution!
Add functions to TestCase
protected $nestedViewData = array();
public function registerNestedView($view)
{
View::composer($view, function($view){
$this->nestedViewsData[$view->getName()] = $view->getData();
});
}
/**
* Assert that the given view has a given piece of bound data.
*
* #param string|array $key
* #param mixed $value
* #return void
*/
public function assertNestedViewHas($view, $key, $value = null)
{
if (is_array($key)) return $this->assertNestedViewHasAll($view, $key);
if ( ! isset($this->nestedViewsData[$view]))
{
return $this->assertTrue(false, 'The view was not called.');
}
$data = $this->nestedViewsData[$view];
if (is_null($value))
{
$this->assertArrayHasKey($key, $data);
}
else
{
if(isset($data[$key]))
$this->assertEquals($value, $data[$key]);
else
return $this->assertTrue(false, 'The View has no bound data with this key.');
}
}
/**
* Assert that the view has a given list of bound data.
*
* #param array $bindings
* #return void
*/
public function assertNestedViewHasAll($view, array $bindings)
{
foreach ($bindings as $key => $value)
{
if (is_int($key))
{
$this->assertNestedViewHas($view, $value);
}
else
{
$this->assertNestedViewHas($view, $key, $value);
}
}
}
public function assertNestedView($view)
{
$this->assertArrayHasKey($view, $this->nestedViewsData);
}
Now you would rewrite your test
$view='orders';
$this->registerNestedView($view);
$this->call('GET', 'orders');
$this->assertNestedViewHas($view, 'orders');
assertNestedViewHas() has all the same feautures as assertViewHas() !
I added another function assertNestedView() which just check if given view was called.
Is there a way to require an array of elements in the rules() method of a Yii model?
For example:
public function rules()
{
return array(
array('question[0],question[1],...,question[k]','require'),
);
}
I have been running into situations where I need to validate several arrays of elements
coming from a form and I can't seem to find a good way of going about it other than doing the above. I have the same problem when specifying attributeLables(). If anyone has some advice or a better way of doing this I would really appreciate it.
You can use the CTypeValidator aliased by type
public function rules()
{
return array(
array('question','type','type'=>'array','allowEmpty'=>false),
);
}
With array('question','type','type'=>'array','allowEmpty'=>false), you can just verify that you receive exactly array, but you don't know what inside this array. To validate array elements you should do something like:
<?php
class TestForm extends CFormModel
{
public $ids;
public function rules()
{
return [
['ids', 'arrayOfInt', 'allowEmpty' => false],
];
}
public function arrayOfInt($attributeName, $params)
{
$allowEmpty = false;
if (isset($params['allowEmpty']) and is_bool($params['allowEmpty'])) {
$allowEmpty = $params['allowEmpty'];
}
if (!is_array($this->$attributeName)) {
$this->addError($attributeName, "$attributeName must be array.");
}
if (empty($this->$attributeName) and !$allowEmpty) {
$this->addError($attributeName, "$attributeName cannot be empty array.");
}
foreach ($this->$attributeName as $key => $value) {
if (!is_int($value)) {
$this->addError($attributeName, "$attributeName contains invalid value: $value.");
}
}
}
}