Related
I have registered yii\web\JsonParser as an application request’s parser in order to enable JSON input:
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
]
Now, I want my custom action to handle JSON requests like the following one:
{
"one": 11,
"two": 2,
"three": 33
}
I am doing this by using bodyParams (source of this idea):
$bodyParams = Yii::$app->request->bodyParams;
$one = $bodyParams['one'] ?? false;
$two = $bodyParams['two'] ?? false;
$three = $bodyParams['three'] ?? false;
The whole controller's code looks like this:
<?php
namespace api\controllers;
use Yii;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'common\models\User';
public function verbs()
{
$verbs = parent::verbs();
$verbs['test'] = ['GET'];
return $verbs;
}
public function actionTest()
{
$bodyParams = Yii::$app->request->bodyParams;
$one = $bodyParams['one'] ?? false;
$two = $bodyParams['two'] ?? false;
$three = $bodyParams['three'] ?? false;
return [
'params' => [
'one' => $one,
'two' => $two,
'three' => $three,
]
];
}
}
Is there a better way of doing so (i.e. reading JSON directly as action's parameters etc.)?
In model I have defined multiple scenarios:
public function rules() {
return [
[['in_quantity'], 'required','on'=>['stockIn']],
[['out_quantity'], 'required','on'=>['stockOut']],
];
}
Is it possible to use both scenario stockIn and stockOut for single model validation?
$StockModel->scenario[] = 'stockOut';
$StockModel->scenario[] = 'stockIn';
or
$StockModel->scenario = ['stockOut','stockIn'];
You can't have multiple scenarios for model. But you can have multiple scenarios for rule:
public function rules() {
return [
[['in_quantity'], 'required', 'on' => ['stockIn', 'stockOut']],
[['out_quantity'], 'required', 'on' => ['stockIn', 'stockOut']],
];
}
If you need multiple scenarios for model, it means that you're overusing scenarios feature.
Also note that it is not recommended to use too many scenarios in one model - scenarios work fine for simple cases, but more complicated cases should be handled by separate models for each scenario.
You can create multiple scenarios this way in model
class MyModel extends \yii\db\ActiveRecord {
const SCENARIO_CREATE = 'scenario_create';
const SCENARIO_UPDATE = 'scenario_update';
// get scenarios
public function scenarios()
{
return [
self::SCENARIO_CREATE => ['user_id', 'name', 'desc', 'published','date_create'],
self::SCENARIO_UPDATE => ['user_id', 'name', 'desc', 'date_update'],
];
}
public function rules()
{
[['user_id'], 'integer'],
[['name','desc'], 'string', 'max' => 70],
[['date_create', 'date_update'], 'date', 'format' => 'php:Y-m-d H:i:s'],
];
}
}
and you can use this way anywhere
public function actionIndex() {
$model = new MyModel;
$model->scenario = MyModel::SCENARIO_CREATE;
if ($model->load(\Yii::$app->request->post())){
if($model->save()){
// some operations
}
}
}
You could if you extend the rule with when for server validation:
[
['in_quantity'],
'required',
'when' => function ($model) {
return $model->scenario === 'stockIn' || $model->scenario === 'stockOut';
}
]
Also if you want to validate in the form (aka client side validation) you could also use the whenClient that expect a js function:
'whenClient' => "function (attribute, value) {
const scenario = $('#stock-scenario').val()
return scenario === 'stockIn' || scenario = 'stockOut';
}"
I am creating one API. In that I want to show buyers info, urls for their files/ images, and count in response. I have 3 tables buyers(PK: buyers_id), filedocs(FK: buyers_id), give_credit_transaction_master(FK: buyers_id). In those tables common column is buyers_id.
CODE
public function index()
{
$filedocsObj = FileDoc::with(['relBuyers'])->where('is_active','1')->get();
//return $filedocsCount;
$info = [];
// $profile_urls=[];
// $aadhar_urls=[];
// $pan_urls=[];
// $transaction_count=[];
// $info = array();
for($i = 0; $i < count($filedocsObj); $i++){
$buyer = FileDoc::Join('buyers', 'file_docs.buyers_id', '=', 'buyers.buyers_id')
->where('file_docs.buyers_id',$filedocsObj[$i]->buyers_id)
->select(
'buyers.buyers_id',
'buyers.buyers_name',
'buyers.buyers_address',
'buyers.buyers_contact_number',
'buyers.buyers_aadhar_number',
'buyers.buyers_pan_number',
'file_docs.buyers_profile_image',
'file_docs.buyers_aadhar_file',
'file_docs.buyers_pan_file',
)
->first();
// $info = $buyer->toArray();
array_push($info, [
'info' => $buyer
]);
// $info[] = $buyer;
$existProfile = Storage::disk('local')->exists('public/uploads/profile_images/'.$filedocsObj[$i]->buyers_profile_image);
if (isset($filedocsObj[$i]->buyers_profile_image) && $existProfile) {
// $profile_urls[$i] = Storage::disk('public')->url('/uploads/profile_images/'.$filedocsObj[$i]->buyers_profile_image);
array_push($info, [
'profile_url' => Storage::disk('public')->url('/uploads/profile_images/'.$filedocsObj[$i]->buyers_profile_image)
]);
//$info[] = Storage::disk('public')->url('/uploads/profile_images/'.$filedocsObj[$i]->buyers_profile_image);
}
else
{
// $profile_urls[$i]="";
array_push($info, [
'profile_url' => ''
]);
// $info[] = "";
}
$existAadhar = Storage::disk('local')->exists('public/uploads/aadhar_files/'.$filedocsObj[$i]->buyers_aadhar_file);
if (isset($filedocsObj[$i]->buyers_aadhar_file) && $existAadhar) {
//$aadhar_urls[$i] = Storage::disk('public')->url('/uploads/aadhar_files/'.$filedocsObj[$i]->buyers_aadhar_file);
array_push($info, [
'aadhar_url' => Storage::disk('public')->url('/uploads/aadhar_files/'.$filedocsObj[$i]->buyers_aadhar_file)
]);
// $info[] = Storage::disk('public')->url('/uploads/aadhar_files/'.$filedocsObj[$i]->buyers_aadhar_file);
}
$existPan = Storage::disk('local')->exists('public/uploads/pan_files/'.$filedocsObj[$i]->buyers_pan_file);
if (isset($filedocsObj[$i]->buyers_pan_file) && $existPan) {
//$pan_urls[$i] = Storage::disk('public')->url('/uploads/pan_files/'.$filedocsObj[$i]->buyers_pan_file);
array_push($info, [
'pan_url' => Storage::disk('public')->url('/uploads/pan_files/'.$filedocsObj[$i]->buyers_pan_file)
]);
//$info[] = Storage::disk('public')->url('/uploads/pan_files/'.$filedocsObj[$i]->buyers_pan_file);
}
$buyerTransactions = GiveCreditTransactionMaster::where('buyers_id',$filedocsObj[$i]->buyers_id)->get();
array_push($info, [
'transaction_count' => count($buyerTransactions)
]);
// $transaction_count[$i] = count($buyerTransactions);
// $resultSet = array_merge($info,$profile_urls,$aadhar_urls,$pan_urls,$transaction_count);
}
return $this->sendResponse($info, 'Buyers retrieved successfully.');
// return $this->sendResponse($resultSet, 'Buyers retrieved successfully.');
// return $this->sendResponse(array("Info" => $filedocs->toArray(),"profile_path" => $profile_urls, "aadhar_urls" => $aadhar_urls, "pan_urls" => $pan_urls, "transactionCountArray" => $transactionCountArray), 'Buyers retrieved successfully.');
}
In above code, I have taken one array info in which I am pushing query result buyer , iteration result profile_url, aadhar_url, pan_url, and another query result counts transaction_count. And returning info array as response.
MY API response:
{
"success": true,
"data": [
{
"info": {
"buyers_id": 2,
"buyers_name": "uuu",
"buyers_address": "dfgfgf",
"buyers_contact_number": "8986665576",
"buyers_aadhar_number": "654654654545",
"buyers_pan_number": "tytyr43242",
"buyers_profile_image": "2_B_profile_lady_profile.png",
"buyers_aadhar_file": "2_B_aadhar_aadhar_card_image.png",
"buyers_pan_file": "2_B_pan_pan_image.jpg"
}
},
{
"profile_url": "http://localhost/storage/uploads/profile_images/2_B_profile_lady_profile.png"
},
{
"aadhar_url": "http://localhost/storage/uploads/aadhar_files/2_B_aadhar_aadhar_card_image.png"
},
{
"pan_url": "http://localhost/storage/uploads/pan_files/2_B_pan_pan_image.jpg"
},
{
"transaction_count": 2
},
{
"info": {
"buyers_id": 28,
"buyers_name": "lili",
"buyers_address": "hjkhkdfgf",
"buyers_contact_number": "7856564656",
"buyers_aadhar_number": "343435353545",
"buyers_pan_number": "trtre34343",
"buyers_profile_image": "28_B_profile_test_profile.png",
"buyers_aadhar_file": "28_B_aadhar_test_aadhar.jpg",
"buyers_pan_file": "28_B_pan_test_pan.jpg"
}
},
{
"profile_url": "http://localhost/storage/uploads/profile_images/28_B_profile_test_profile.png"
},
{
"aadhar_url": "http://localhost/storage/uploads/aadhar_files/28_B_aadhar_test_aadhar.jpg"
},
{
"pan_url": "http://localhost/storage/uploads/pan_files/28_B_pan_test_pan.jpg"
},
{
"transaction_count": 0
}
],
"message": "Buyers retrieved successfully."
}
But in above response I am getting info of particular buyer separately than profile_url, aadhar_url, pan_url, transaction_count. Also profile_url, aadhar_url, pan_url, transaction_count this are getting separately.
I want all parameters(info,profile_url, aadhar_url, pan_url, transaction_count) of one buyer should come in one {}. How can I get that type of response?
I tried a lot using array_push, array_merge etc. But not getting requied response.
Please help. Thanks in advance.
This will help you with your desired response. I have manged your function. make it try and let me know if it helps you thanks
public
function index()
{
$filedocsObj = FileDoc::with(['relBuyers'])->where('is_active', '1')->get();
//return $filedocsCount;
$info = [];
// $profile_urls=[];
// $aadhar_urls=[];
// $pan_urls=[];
// $transaction_count=[];
// $info = array();
foreach ($filedocsObj as $index => $filedocsObjInfo) {
$buyer = FileDoc::Join('buyers', 'file_docs.buyers_id', '=', 'buyers.buyers_id')
->where('file_docs.buyers_id', $filedocsObjInfo->buyers_id)
->select(
'buyers.buyers_id',
'buyers.buyers_name',
'buyers.buyers_address',
'buyers.buyers_contact_number',
'buyers.buyers_aadhar_number',
'buyers.buyers_pan_number',
'file_docs.buyers_profile_image',
'file_docs.buyers_aadhar_file',
'file_docs.buyers_pan_file',
)
->first();
// $info = $buyer->toArray();
$info[$index]['info'] = $buyer;
// $info[] = $buyer;
$existProfile = Storage::disk('local')->exists('public/uploads/profile_images/' . $filedocsObjInfo->buyers_profile_image);
if (isset($filedocsObjInfo->buyers_profile_image) && $existProfile) {
$info[$index]['profile_url'] = Storage::disk('public')->url('/uploads/profile_images/' . $filedocsObjInfo->buyers_profile_image);
} else {
$info[$index]['profile_url'] = '';
}
$existAadhar = Storage::disk('local')->exists('public/uploads/aadhar_files/' . $filedocsObjInfo->buyers_aadhar_file);
if (isset($filedocsObjInfo->buyers_aadhar_file) && $existAadhar) {
$info[$index]['aadhar_url'] = Storage::disk('public')->url('/uploads/aadhar_files/' . $filedocsObjInfo->buyers_aadhar_file);
}
$existPan = Storage::disk('local')->exists('public/uploads/pan_files/' . $filedocsObjInfo->buyers_pan_file);
if (isset($filedocsObjInfo->buyers_pan_file) && $existPan) {
$info[$index]['pan_url'] = Storage::disk('public')->url('/uploads/pan_files/' . $filedocsObjInfo->buyers_pan_file);
}
$buyerTransactions = GiveCreditTransactionMaster::where('buyers_id', $filedocsObjInfo->buyers_id)->get();
$info[$index]['transaction_count'] = count($buyerTransactions);
// $transaction_count[$i] = count($buyerTransactions);
// $resultSet = array_merge($info,$profile_urls,$aadhar_urls,$pan_urls,$transaction_count);
}
return $this->sendResponse($info, 'Buyers retrieved successfully.');
// return $this->sendResponse($resultSet, 'Buyers retrieved successfully.');
// return $this->sendResponse(array("Info" => $filedocs->toArray(),"profile_path" => $profile_urls, "aadhar_urls" => $aadhar_urls, "pan_urls" => $pan_urls, "transactionCountArray" => $transactionCountArray), 'Buyers retrieved successfully.');
}
on PHP the following solution might work.
json_encode(array_merge(json_decode($a, true),json_decode($b, true)))
Try and let me know.
When you use an array_merge try json_decode your value like above.
It's the first time i am using validation in laravel. I am trying to apply validation rule on below json object. The json object name is payload and example is given below.
payload = {
"name": "jason123",
"email": "email#xyz.com",
"password": "password",
"gender": "male",
"age": 21,
"mobile_number": "0322 8075833",
"company_name": "xyz",
"verification_status": 0,
"image_url": "image.png",
"address": "main address",
"lattitude": 0,
"longitude": 0,
"message": "my message",
"profession_id": 1,
"designation_id": 1,
"skills": [
{
"id": 1,
"custom" : "new custom1"
}
]
}
And the validation code is like below, for testing purpose i am validating name as a digits. When i executed the below code, the above json object is approved and inserted into my database. Instead, it should give me an exception because i am passing name with alpha numeric value, am i doing something wrong:
public function store(Request $request)
{
$this->validate($request, [
'name' => 'digits',
'age' => 'digits',
]);
}
Please try this way
use Validator;
public function store(Request $request)
{
//$data = $request->all();
$data = json_decode($request->payload, true);
$rules = [
'name' => 'digits:8', //Must be a number and length of value is 8
'age' => 'digits:8'
];
$validator = Validator::make($data, $rules);
if ($validator->passes()) {
//TODO Handle your data
} else {
//TODO Handle your error
dd($validator->errors()->all());
}
}
digits:value
The field under validation must be numeric and must have an exact length of value.
I see some helpful answers here, just want to add - my preference is that controller functions only deal with valid requests. So I keep all validation in the request. Laravel injects the request into the controller function after validating all the rules within the request. With one small tweak (or better yet a trait) the standard FormRequest works great for validating json posts.
Client example.js
var data = {first: "Joe", last: "Dohn"};
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST",'//laravel.test/api/endpoint');
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send(JSON.stringify(data));
project/routes/api.php
Route::any('endpoint', function (\App\Http\Requests\MyJsonRequest $request){
dd($request->all());
});
app/Http/Requests/MyJsonRequest.php (as generated by php artisan make:request MyJsonRequest)
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyJsonRequest extends FormRequest{
public function authorize(){
return true;//you'll want to secure this
}
public function rules(){
return [
'first' => 'required',
'last' => 'required|max:69',
];
}
//All normal laravel request/validation stuff until here
//We want the JSON...
//so we overload one critical function with SOMETHING LIKE this
public function all($keys = null){
if(empty($keys)){
return parent::json()->all();
}
return collect(parent::json()->all())->only($keys)->toArray();
}
}
Your payload should be payload: { then you can do
$this->validate($request->payload, [
'name' => 'required|digits:5',
'age' => 'required|digits:5',
]);
or if you are not sending the payload key you can just use $request->all()
$request->merge([
'meta_data' => !is_null($request->meta_data) ? json_encode($request->meta_data) : null
]);
validator = Validator::make($request->all(), [
'meta_data' => 'nullable|json'
]);
Use the Validator factory class instead using validate method derived from controller's trait. It accepts array for the payload, so you need to decode it first
\Validator::make(json_decode($request->payload, true), [
'name' => 'digits',
'age' => 'digits',
]);
Following the example of #tarek-adam, in Laravel 9 it would be:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyJsonRequest extends FormRequest{
public function authorize(){
return true;//you'll want to secure this
}
public function rules(){
return [
'first' => 'required',
'last' => 'required|max:69',
];
}
//All normal laravel request/validation stuff until here
//We want the JSON...
//so we overload one critical function with SOMETHING LIKE this
public function validationData()
{
if(empty($this->all())){
$res = [
'success' => false,
'message' => 'Check your request',
];
throw new HttpResponseException(
response()->json($res, 422)
);
}
return $this->all();
}
}
I have a Symfony3 Application setup and would like to rebuild the frontend based on React now.
One of the Entities is User and each of them can have one or more Groups so in the HTML form a list of Checkboxes appears, so the admin can select the groups attached to a User.
In UserType.php this looks like that:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class)
->add('password', TextType::class)
->add('email', EmailType::class)
->add('groups', EntityType::class, [
'class' => Group::class,
'choice_label' => 'name',
'expanded' => true,
'multiple' => true//,
//'data' => $builder->getData()->getGroups()
]);
}
To render the Form using React, it would be extremely handy to get a JSON response which could look like that:
{
"user": {
…
"groups": [<gid 1>, …]
"groups_available": [
{
"id": <gid 1>,
"name": …
},
…
]
}
}
So that the groups array contains all the ids of the groups, the user is attached to and groups_available a list of all available groups.
Right now I am using FOSRestBundle and in the Controller it looks like that:
public function getUserformAction($id=null)
{
//if the id is null, create a new user
//else get the existing one
…
$form = $this->createForm(UserType::class, $user);
$view = $form->createView();
return $this->handleView($view);
}
How can I do that?
you should try the following code:
->add('groups', EntityType::class, array(
//if Group is in AppBundle or use the required Bundle name
'class' => 'AppBundle:Group',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.name', 'ASC')
},
'choice_label' => 'name',
'multiple' => true,
'expanded' => true,
));
You can also get a reference from here
After digging in the source and with the help of the debugger I could manage to do it in a more less robust and generic way like so:
protected function getFormStructure(Form $form)
{
return $this->iterateFormview($form->createView(), []);
}
private function iterateFormview(FormView $view, array $result)
{
foreach($view as $child) {
$vars = $child->vars;
$data = ['value' => $vars['value']];
if(isset($vars['choices'])) {
$data['choices'] = [];
foreach ($vars['choices'] as $choice) {
array_push($data['choices'], [
'label' => $choice->label,
'value' => $choice->value]);
}
}
$result[$vars['full_name']] = $data;
if(count($child) > 0) {
$result = $this->iterateFormview($child, $result);
}
}
return $result;
}
Result (as json):
{
…
"user[groups]":
{
"value": "",
"choices": [
{
"value": 100,
"label": "the name"
},
…
]
}
}
I guess this routine needs to be extended if I need to support more types… But for now this will do it.