There is a database field called path it is text type and it is used to store paths marking strings as CSV, something like the following:
*:*,site/about,site/*
Originally, it is used as a text input field like the following:
<?= $form->field($model, 'path')->textarea(['rows' => 6]) ?>
I want to, temporary, convert those paths into a dropDownSelect list in the view, then in the controller I will implode the selected items into string again to be validated and saved.
In the view _form.php I have the following:
<?= $form->field($model, 'path')->dropDownList($model->getPathSelectList($model->path), ['multiple' => 'multiple']) ?>
In the controller:
public function actionCreate()
{
$model = new StaticHtml();
// var_dump($model->path);
// die();
if (is_array($model->path)) $model->path = implode(',', $model->path);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
$model->path = $model->getPathsList(Yii::$app->getRequest()->getQueryParam('path'));
return $this->render('create', [
'model' => $model,
]);
}
}
Please Notice the two commented lines var_dump($model->path) and die() I set them to debug, because every time I select from the dropDownList and submit the form, the validation fails and tell me that Path must be a string and of course there is no any selection reserved unlike other fields and the var_dump($model->path) prints NULL:
In the code above, there is two model's methods:
getPathsList() it is called from the controller and it convert an initial path to path marks and set them as csv string.
getPathSelectList in the view and it convert the csv into an associative array which its keys and values are equals for each one to be data list for the dropDownList field.
Till this point, I think it will need more for update, but however, What's the problem in my code that prevents posting the $model->path so it prints NULL with var_dump()?
I'm pretty sure that it is a problem in receiving data from the form because I tried to:
var_dump(Yii::$app->getRequest()->post('path'));
var_dump(Yii::$app->getRequest()->post('title'));
var_dump(Yii::$app->request->post('path'));
and all of them returns NULL
if your validation rule you can add
skipOnEmpty => true
Otherwise you can override the check this way
['path', 'required', 'isEmpty' => function ($value) {
return empty($value);
}]
Related
I have a very simple view which contains a form. This form contains 2 select fields populated with data from the database.
Let's say the first select contains "Banana", "Apple", "Orange". The second one contains "Burger", "Pizza", "Hot Dog".
I'm submitting the form (POST) and saving the combination in a table of my database. Before actually saving, I'm checking if the combination doesn't already exist. I don't want to save "Banana - Burger" twice.
If the combination already exists I'm returning the following:
session(['error' => 'This combination already exists.']);
return redirect()->back()->withInput();
This brings me back to my form, and shows the error message in a popup.
How can I return the proper HTTP code (409 in this instance)?
You can make changes in your laravel validation for that. Replace your table name and field name in below example.
$data = $request->all();
$request->validate([
'fruits' => [
'required',
Rule::exists('fruits')->where(function ($query) {
$query->where('fruit_name', $data['fruit_name']);
}),
],
'foods' => [
'required',
Rule::exists('foods')->where(function ($query) {
$query->where('food_name', $data['food_name']);
})
]
]);
Hope this will help you.
I am trying to understand how sorting works in GridView by means of generating a default CRUD application. Sorting happens after clicking the respective attribute which is the table header. The column name is attached to the url with the variable sort and on click the action method is invoked, but what I am wondering is that the action method which is mentioned in the url with the actual variable $sort is not present in the controller.
Below is a example
The url looks like the below,
/advanced/frontend/web/index.php?r=site%2Findex&sort=customer_user_name2
But there is no corresponding action method in the site controller as
function actionIndex($sort);
Consider following example
We have Passenger CRUD
Consider Passenger - a model and PassengerSearch - its corresponding search model
Passenger attribute are id, name and country_name
In PassengerController.php
<?php
...
public function actionIndex()
{
$searchModel = new PassengerSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
...
?>
Search string a in name column
and sort id column in desc order
Look at the url generated, it would be like
`http://localhost/YII2proj/passenger/index?PassengerSearch%5Bid%5D=&PassengerSearch%5Bname%5D=a&PassengerSearch%5Bcountry_name%5D=&sort=-id`
In human readable format
http://localhost/YII2proj/passenger/index?
PassengerSearch[id]=&
PassengerSearch[name]=a&
PassengerSearch[country_name]=&
sort=-id
Observe PassengerSearch is a array. Please refer - How to send a array in url request?
Focus on the parameter to search() method of object PassengerSearch class it is Yii::$app->request->queryParams
Before
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
line, insert following code
echo '<pre>';
print_r(Yii::$app->request->queryParams);
echo '</pre>';
die;
and see the result
Array
(
[PassengerSearch] => Array
(
[id] =>
[name] => a
[country_name] =>
)
[sort] => -id
)
If no searching sorting done, then this array will be empty
So this line does your code like following
<?php
if(isset($_GET['sort']) || isset($_GET['PassengerSearch']['id'] ...)
{
// validate it because what if I changed url like
// `http://localhost/YII2proj/passenger/index?PassengerSearcasdh[dfsid]=&PassengerSearch[name]=a&PassengerSearch[country_name]=&sorsfdft=-idadlashdfhls`
// ie some scrap value
// If error returned from validate function, default sorting is done, and malformed search param is not set
// if success, fill up that array
}
?>
Then in search Model load() is the hero.
Where and how above code is done? - Refer Model and load()
Searching is done via andFilterWhere after loading ie load() and that you can see in search() method
Hope you got the idea behind searching and sorting.
I followed the post on setting up an editable field in a grid here http://webtips.krajee.com/setup-editable-column-grid-view-manipulate-records/
Everything seems to work - I click on the field, change it's value and it shows the updated value in the grid. However - next time I refresh, the value reverts to the original value.
I've done a little debugging, and it would appear that the save in the controller is failing.
My table has a two key fields (key1 and key2) and a value field. I am trying to update this value field.
My view code is as follows:
$gridColumns = [
'AnotherField',
[
'class' => 'kartik\grid\EditableColumn',
'attribute'=>'value',
'label' => 'some value',
'editableOptions'=> [
'header' => 'profile',
'format' => Editable::FORMAT_BUTTON,
]
];
...
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => $gridColumns
]) ?>
and the controller code is as follows:
<snip>
//default code from gii-generated controller
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// orig post said to use this: $dataProvider = $searchModel->search(Yii::$app->request->getQueryParams());
// Validate if there is an editable input saved via AJAX
if (Yii::$app->request->post('hasEditable')) {
$model = new MyTable;
$post = [];
$posted = current($_POST['MyTable']);
$post['MyTable'] = $posted;
// Load model like any single model validation
if ($model->load($post)) {
// When doing $result = $model->save(); I get a return value of false
$model->save();
$out = Json::encode(['output'=>$output, 'message'=>'']);
}
// Return AJAX JSON encoded response and exit
echo $out;
return;
}
// Non-AJAX - render the grid by default
</snip>
How does the controller know which record to update as I am only passing in the 'value' field, not the keys?
Do I somehow need to add these key1 and key2 fields to the view within the editable widget in order to get these values passed to the controller (so that the load and save know which record to update) or is there some other way that the controller handles this?
In the example on this page http://demos.krajee.com/grid-demo (you can see the column definition too) you can see with firebug that the following values are sent:
Book[5][buy_amount] 1
_csrf MEQ0NnEzNjFhd2dBB2wBCHN0cGVCA3lfZHF1TDReRlAAcQNUPUFjBQ==
editableIndex 5
editableKey 6
hasEditable 1
I believe the 5 in Book[5] is probably matching the index of the record.
This part does that from what I see
$post = [];
$posted = current($_POST['Book']);
$post['Book'] = $posted;
I have never used the widget so I might be wrong.
I have found the solution with some experimentation. I had to unserialize the $editableKey value and extract my key field values from it. I then call 'findModel' to load in the record to be updated.
The updated controller code is as follows:
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// Validate if there is an editable input saved via AJAX
if (Yii::$app->request->post('hasEditable')) {
$keys = unserialize(Yii::$app->request->post('editableKey'));
$model = $this->findModel($keys['key1'], $keys['key2']);
// Store a default JSON response as desired by editable
$out = Json::encode(['output'=>'', 'message'=>'']);
// Fetch the first entry in posted data (there should only be one
// entry anyway in this array for an editable submission)
$values = current($_POST['MyTable']);
// Load model like any single model validation
if ($model->key1) {
// Update the Profile with the new value passed in
$model->value = $values['value'];
$model->save();
...
Thanks for the advice
I was also looking for a solution of updating table using Editable column. Looking at the ajax post I got similar like this output:
Book[5][buy_amount] 1
_csrf MEQ0NnEzNjFhd2dBB2wBCHN0cGVCA3lfZHF1TDReRlAAcQNUPUFjBQ==
editableIndex 5
editableKey 6
hasEditable 1
Here I found the value of editableKey is the value of id of the table.
So to update specific row on gridview column I used the editableKey and update based on it.
In controller :
if (Yii::$app->request->post('hasEditable')) {
$_id=$_POST['editableKey'];
$model = $this->findModel($_id);
$post = [];
$posted = current($_POST['MyTable']);
$post['MyTable'] = $posted;
// Load model like any single model validation
if ($model->load($post)) {
// When doing $result = $model->save(); I get a return value of false
$model->save();
if (isset($posted['buy_amount']))
{
$output = $model->buy_amount;
}
$out = Json::encode(['output'=>$output, 'message'=>'']);
}
// Return AJAX JSON encoded response and exit
echo $out;
return;
}
I just get the post data using $_id=$_POST['editableKey'];
and set my $model as $model = $this->findModel($_id);
now you can update multiple editable column just adding condition like this
if (isset($posted['buy_amount']))
{
$output = $model->buy_amount;
}
Thanks!
In my form, I created the value by populating the dropbox from values from a table.
<?php echo $form->dropDownList($model,'status', CHtml::listData(Statusprospect::model()->findAll(), 'id', 'status'),array('prompt' => 'Select')); ?>
When I view the record it has a 1, as it should for status. How do I make it display the value when the record is viewed, instead of the 1.
The view file code that currently displays the field is this:
<?php echo CHtml::encode($data->status); ?>
The Model does have the relationship defined:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'status0' => array(self::BELONGS_TO, 'Statusprospect', 'status'),
);
}
How would I accomplish showing the value instead of the number?
Right now this should work $data->status0->status.
Take care that $data->status0->status might not be set if $data->status can be null so make a check beforehand if that is the case. You can use
CHtml::encode(isset($data->status0->status) ? $data->status0->status : '-');
I am able to view and delete the data by passing ID in the URl in format of:
/apis/view/id.json
using:
public function view($id) {
$api = $this->Api->findById($id);
$this->set(array(
'api' => $api,
'_serialize' => array('api')
));
}
Similarly I want to implement add and edit, where I can pass data in Json format in the HTTP body and store/edit it in the database.
I couldn't follow the this solution:
CakePHP API PUT with JSON input
I couldn't understand how to use
$data = $this->request->input('json_decode');
to achieve it.
Add can simply be used as given in documentation by appending .json to it. The URL at which you post the data will become /apis.json. This will automatically access the add() method.
Assuming you pass json values email and password in such format: {"email":"abc#def.com","password":"123456"}
public function add(){
$data=$this->request->input('json_decode', true ); //$data stores the json
//input. Remember, if you do not add 'true', it will not store in array format.
$data = $this->Api->findByEmailAndPassword($data['email'],$data['password']);
//simple check to see if posted values match values in model "Api".
if($data) {$this->set(array(
'data' => $data,
'_serialize' => array('data')));}
else{ $this->set(array(
'data' => "sorry",
'_serialize' => array('data')));}
}// The last if else is to check if $data is true, ie. if value matches,
// it will send the input values back in JSON response. If the email pass
// is not found, it will return "Sorry" in json format only.
Hope that answers your question! Put is also very similar, except it will check if the data exists, if it doesn't it will create or else it will modify existing data. If you have any further doubts don't hesitate to ask :)
As explained in the linked documentation, CakeRequest::input() reads the raw input data, and optionally passes it through a decoding function.
So $this->request->input('json_decode') gives you the decoded JSON input, and in case it's formatting follows the Cake conventions, you can simply pass it to one of Model save methods.
Here's a very basic (untested) example:
public function add()
{
if($this->request->is('put'))
{
$data = $this->request->input('json_decode', true);
$api = $this->Api->save($data);
$validationErrors => $this->Api->validationErrors;
$this->set(array
(
'api' => $api,
'validationErrors' => $validationErrors,
'_serialize' => array('api', 'validationErrors')
));
}
}
This will try to save the data and return the save result as well as possible validation errors.
In case the formatting of the input data doesn't follow the Cake conventions, you'll have to transform it accordingly.