I am using a calendar in my project and I want to pass data from my Event model to view file in JSON format. I tried following but it didn't work and am not able to display the data properly
$events = Event::find()->where(1)->all();
$data = [];
foreach ($events AS $model){
//Testing
$data['title'] = $time->title;
$data['date'] = $model->start_date;
$data['description'] = $time->description;
}
\Yii::$app->response->format = 'json';
echo \yii\helpers\Json::encode($data);
But it only returns one model in that $data array, the final data should be in following format:
[
{"date": "2013-03-19 17:30:00", "type": "meeting", "title": "Test Last Year" },
{ "date": "2013-03-23 17:30:00", "type": "meeting", "title": "Test Next Year" }
]
When you write this:
\Yii::$app->response->format = 'json';
before rendering data, there is no need to do any additional manipulations for converting array to JSON.
You just need to return (not echo) an array:
return $data;
An array will be automatically transformed to JSON.
Also it's better to use yii\web\Response::FORMAT_JSON constant instead of hardcoded string.
Another way of handling that will be using ContentNegotiator filter which has more options, allows setting of multiple actions, etc. Example for controller:
use yii\web\Response;
...
/**
* #inheritdoc
*/
public function behaviors()
{
return [
[
'class' => 'yii\filters\ContentNegotiator',
'only' => ['view', 'index'], // in a controller
// if in a module, use the following IDs for user actions
// 'only' => ['user/view', 'user/index']
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
],
];
}
It can also be configured for whole application.
Update: If you are using it outside of controller, don't set response format. Using Json helper with encode() method should be enough. But there is also one error in your code, you should create new array element like this:
$data = [];
foreach ($events as $model) {
$data[] = [
'title' => $time->title,
'date' => $model->start_date,
'description' => $time->description,
];
}
You can try like this:
$events = Event::find()->select('title,date,description')->where(1)->all()
yii::$app->response->format = yii\web\Response::FORMAT_JSON; // Change response format on the fly
return $events; // return events it will automatically be converted in JSON because of the response format.
Btw you are overwriting $data variable in foreach loop you should do:
$data = [];
foreach ($events AS $model){
//Make a multidimensional array
$data[] = ['time' => $time->title,'date' => $model->start_date,'description' => $time->description];
}
echo \yii\helpers\Json::encode($data);
Related
I'm using Laravel 5.7. and GuzzleHttp 6.0 to get API response
from endpoint
I'm passing query data from Blade form to this function.
public static function prhmulti($multisearch, $start ,$end)
{ $city = $multisearch['city'];
$client = new Client([
'base_uri' => 'https://avoindata.prh.fi/tr/',
'query' => [
'totalResults' => 'true',
'maxResults' => '1000',
'registeredOffice'=> $city,
'companyForm'=>'OY',
'companyRegistrationFrom'=>$start,
'companyRegistrationTo'=>$end,
],
'defaults'=>[
'timeout' => 2.0,
'cookies' => true,
'headers' => [
'content-type' => 'application/json',
'User-Agent' =>"GuzzleHttp/Laravel-App-5.7, Copyright MikroMike"
]]]);
$res = $client->request('GET','v1');
$ResData = json_decode($res->getBody()->getContents());
dd ($ResData) gives all data from API response.
But I am not able to return JSON back to other function
return $this->multisave($ResData);
public static function multisave (data $ResData)
This will parse JSON and
{
foreach ($data->results as $company) {
$name = $company->name;
$Addr = $company->addresses;
$businessId = $company->businessId;
$companyForm = $company->companyForm;
$registrationDate = $company->registrationDate;
foreach ($company->addresses as $Addr) {
$city = $Addr->city;
$postcode = $Addr->postCode;
$street = $Addr->street;
}
}
save data to Mysql.
$NewCompany = new Company();
$NewCompany = Company::updateOrCreate($array,[
[ 'vat_id', $businessId],
[ 'name', $name],
[ 'form',$companyForm],
[ 'street', $Addr],
[ 'postcode', $postcode],
[ 'city', $city],
[ 'regdate', $registrationDate],
]);
}
IF Parse part and Save part is inside same function code works ok(save only one company),
but I need to separate them because later on it's easier to maintain.
Error which I am getting to return $ResData
" Using $this when not in object context"
Information is in JSON array.
Also foreach part save ONLY one company ?
foreach ($data->results as $company) {
$name = $company->name;
$Addr = $company->addresses;
$businessId = $company->businessId;
$companyForm = $company->companyForm;
$registrationDate = $company->registrationDate;
foreach ($company->addresses as $Addr) {
$city = $Addr->city;
$postcode = $Addr->postCode;
$street = $Addr->street;
}
So : 1) What is best way to create own function for parse JSON
and other for save data to DB?
2) As foreach loop save only one company data, What is
best way to fix it?
Thanks MikroMike.
Resolved my own question for saving companies to db
First get total number inside Array
use for-loop to make counting
use foreach-loop extract information per single company as object.
$data = json_decode($res->getBody()->getContents());
$total = $data->totalResults;
for ($i = 0; $i < $total; $i++){
$NewCompany = new Company();
foreach ($data->results as $company)
{
$name = $company->name;
$businessId = $company->businessId;
$companyForm = $company->companyForm;
$registrationDate = $company->registrationDate;
$array = [];
Arr::set($array, 'vat_id', $businessId);
Arr::set($array, 'name', $name );
Arr::set($array, 'form', $companyForm);
Arr::set($array, 'regdate', $registrationDate);
$NewCompany = Company::updateOrCreate($array,[
[ 'vat_id', $businessId],
[ 'name', $name],
[ 'form',$companyForm],
[ 'regdate', $registrationDate],
]);
}// END OF MAIN FOREACH
}// END OF For loop
}// END OF FUCNTION
} // END OF CLASS
When the JSON format is response, the original sorting of the data is lost. When response in XML, the sort is saved. How can I preserve the original sorting with JSON?
My controller:
use yii\rest\ActiveController;
class DomainController extends ActiveController
{
...
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['corsFilter' ] = [
'class' => \yii\filters\Cors::className(),
];
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
return $behaviors;
}
And action in the controller:
public function actionIndex()
{
$domains = Domain::find()
->leftJoin('WEB_DOMAIN_PRIORITY', 'WEB_DOMAIN_PRIORITY.id = WEB_DOMAIN.priority_id')
->orderBy(['priority' => SORT_DESC])->all();
$test = [];
foreach ($domains as $domain) {
$test[$domain->id] = $domain->title;
}
//echo "<pre>"; print_r($test);die; < -- its ok. right sort
//return $test; < -- its wrong. sort is changed
}
And if i change in behavior this:
'application/json' => \yii\web\Response::FORMAT_JSON,
To:
'application/json' => \yii\web\Response::FORMAT_XML,
I have xml response with right sort.
Only JSON response sorting my array by array keys(ASC).
Here
$test[$domain->id] = $domain->title;
you are adding new array keys. This could change order based on rest/Serializer.
You could apply preserveKeys to serializer as here http://www.yiiframework.com/doc-2.0/yii-rest-serializer.html#$preserveKeys-detail
or don't change keys order.
#Fabrizio Caldarelli not exactly. In this moment response preparing JsonResponseFormatter and it use yii\helpers\Json::encode for format data.
In Yii2 framework is it possible to add a new attribute dynamically to an existing object, which is retrieved from Database?
Example
//Retrieve from $result
$result = Result::findone(1);
//Add dynamic attribute to the object say 'result'
$result->attributes = array('attempt' => 1);
If it is not possible, please suggest an alternate best method to implement it.
Finally I would be converting the result to a json object. In my application, at the behaviour code block, I have used like this:
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
You can add define a public variable inside your model, that will store dynamic attributes as associative array. It'll look something like this:
class Result extends \yii\db\ActiveRecord implements Arrayable
{
public $dynamic;
// Implementation of Arrayable fields() method, for JSON
public function fields()
{
return [
'id' => 'id',
'created_at' => 'created_at',
// other attributes...
'dynamic' => 'dynamic',
];
}
...
..in your action pass some dynamic values to your model, and return everything as JSON:
public function actionJson()
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$model = Result::findOne(1);
$model->dynamic = [
'field1' => 'value1',
'field2' => 2,
'field3' => 3.33,
];
return $model;
}
In result you will get JSON like this:
{"id":1,"created_at":1499497557,"dynamic":{"field1":"value1","field2":2,"field3":3.33}}
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.