I'm using the isDirty() method in my controller to check if any field is changed. Then I am saving the old data of a field and the new data in a table. The code is working fine; however, how can I optimize this code?
By using the below code, I will have to write each field name again and again. If request->all() has 20 fields, but I want to check six fields if they are modified, how can I pass only 6 fields in the below code, without repeating?
Controller
if ($teacher->isDirty('field1')) {
$new_data = $teacher->field1;
$old_data = $teacher->getOriginal('field1');
DB::table('teacher_logs')->insert(
[
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $old_data,
'new_value' => $new_data,
'column_changed' => "First Name",
]);
}
You can set a list of what fields you want to be checking for then you can loop through the dirty fields and build your insert records.
use Illuminate\Support\Arr;
...
$fields = [
'field1' => 'First Name',
'field2' => '...',
...
];
$dirtied = Arr::only($teacher->getDirty(), array_keys($fields));
$inserts = [];
foreach ($dirtied as $key => $value) {
$inserts[] = [
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $teacher->getOriginal($key),
'new_value' => $value,
'column_changed' => $fields[$key];
];
}
DB::table(...)->insert($inserts);
i tried following code after getting idea by lagbox in comments, and i have found solution to my problem.
$dirty = $teacher->getDirty('field1','field2','field3');
foreach ($dirty as $field => $newdata)
{
$olddata = $teacher->getOriginal($field);
if ($olddata != $newdata)
{
DB::table('teacher_logs')->insert(
['user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $olddata,
'new_value' => $newdata,
'column_changed' => "changed",
]);
}
}
Related
I'm using the repeatable, from Laravel-Backpack I can save the data into the two tables. However, I can not load these data when trying to edit a sale. It does not load the data in the form that were saved through the Repeatable.
Example:
When viewing the example of the demo through the link.
https://demo.backpackforelaravel.com/admin/dummy/create
It converts the data from the fields from the REPEATABLE to JSON, and saves in the database in a field called Extra.
Saved format in the extra field in the database:
{
"simple": "[{\"text\":\"TesteTesteTesteTeste\",\"email\":\"admin#admin\",\"textarea\":\"teste\",\"number\":\"1\",\"float\":\"1\",\"number_with_prefix\":\"1\",\"number_with_suffix\":\"0\",\"text_with_both_prefix_and_suffix\":\"1\",\"password\":\"123\",\"radio\":\"1\",\"checkbox\":\"1\",\"hidden\":\"6318\"}]",
}
In my case I am saving the data in different tables without using JSON.
//My Model
class Sales extends Model
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'date_purchase' => 'date',
'client_id' => 'integer',
];
public function getProductsAttribute()
{
$objects = ItensProducts::where('product_id', $this->id)->get();
$array = [];
if (!empty($objects)) {
foreach ($objects as $itens) {
$obj = new stdClass();
$obj->product_id = "" . $itens->product_id;
$obj->quantity = "" . $itens->quantity;
$categoryProduct = CategoryProduct::where('product_id', $itens->product_id)->get();
$arrayCategoryProduct = [];
foreach ($categoryProduct as $stItens) {
$arrayCategoryProduct[] = $stItens->name;
}
$obj->categories_product = $arrayCategoryProduct;
$array[] = $obj;
}
}
//Converts JSON to the example of the extra database field
$array_result = json_encode(\json_encode($array), JSON_HEX_QUOT | JSON_HEX_APOS);
$array_result = str_replace(['\u0022', '\u0027'], ["\\\"", "\\'"], $array_result);
return $array_result;
}
My form:
//SalesCruController.php
protected function setupCreateOperation()
{
CRUD::addField([ // repeatable
'name' => 'products',
'label' => 'Produtos(s)',
'type' => 'repeatable',
'fields' => [
[
'name' => 'product_id', 'type' => 'select2', 'label' => 'Produtos',
'attribute' => "name",
'model' => "App\Models\Product",
'entity' => 'products',
'placeholder' => "Selecione o Produto",
'wrapper' => [
'class' => 'form-group col-md-6'
],
],
[
'name' => 'category_id', 'type' => 'select2', 'label' => "Categoria",
'attribute' => "name",
'model' => "App\Models\CategoryProduct",
'entity' => 'categories',
'placeholder' => "Selecione uma Categoria",
],
[
'name' => 'quantity',
'label' => "Quantidade",
'type' => 'text',
],
],
// optional
'new_item_label' => 'Adicionar',
'init_rows' => 1,
'min_rows' => 1,
'max_rows' => 3,
],);
}
}
Method Store
public function store()
{
$item = $this->crud->create($this->crud->getRequest()->except(['save_action', '_token', '_method']));
$products = json_decode($this->crud->getRequest()->input('products'));
$this->validateRepeatableFields($categoryProduct);
if (is_array($products)) {
foreach ($products as $itens) {
$obj = new ItensProduct();
$obj->sale_id = $item->getKey();
$obj->product_id = $itens->product_id;
$obj->quantity = $itens->quantity;
$obj->save();
$categoryProduct = json_decode($itens->categories);
foreach ($categoryProduct as $cItens) {
$objCat = new CategoryProduct();
$objCat->product_id = $obj->getKey();
$objCat->name = $cItens;
$objCat->save();
}
}
} else {
\Alert::add('warning', '<b>Preencha os campos de pelo menos um produto.</b>')->flash();
return redirect('admin/sales/create');
}
\Alert::success(trans('backpack::crud.insert_success'))->flash();
return redirect('admin/sales');
}
function validateRepeatableFields($categoryProduct)
{
foreach ($categoryProduct as $group) {
Validator::make((array)$group, [
'sale_id' => 'required',
'product_id' => 'required',
'quantity' => 'required',
], [
"product_id.required" => "O campo Produto é obrigatório",
"quantity.required" => "O campo quantidade é obrigatório",
])->validate();
}
}
I was able to solve the problem of returning the fields to the Repeatable form.
I want to share the solution if someone needs it. The error occurred when I tried to put the data from the category_id field which is a vector in a json it has to return like this, as shown below.
{"product_id": "4", quantity: "3", "category_id": "[10, 4, 8, 8]"}
The vector would have to be inside a string, I was passing the fields but was not converting the whole vector to a string in the format that the repeatable expected. Then I conceded the IDs in a string and in the end I conceded with the cohets and used the substring to remove the last virgulation, as the code below shows.
In this way I was able to return the fields with the appropriate information that were saved in the database in the Repeatable form.
public function getProductsAttribute()
{
$objects = ItensProducts::where('product_id', $this->id)->get();
$response = [];
if (!empty($objects)) {
foreach ($objects as $itens) {
$categoryProduct = CategoryProduct::where('product_id', $itens->product_id)->get();
$itensCatProd = '';
foreach ($categoryProduct as $itensCatProd) {
$itensCatProd .= $itensCatProd->id . ',';
};
$response[] = [
'product_id' => $itens->product_id,
'category_id' => '[' . substr($itensCatProd, 0, -1) . ']',
'quantity' => $itens->quantity,
];
return json_encode($response);
}
}
}
I have JSON file and have data in it. I want to import all data in the database through DB seeders. I am getting an error Trying to get property name of non-object. I have multiple data how I can insert in the database?
public function run()
{
$json = File::get("public/kmz/WASASubdivisions.geojson");
$data = json_decode($json);
// dd($data);
foreach ($data as $obj){
Regions::create(array(
'name' => $obj[0]->Name,
'description' => $obj[0]->description,
'altitudeMode' => $obj[0]->altitudeMode,
'Town' => $obj[0]->Town,
'AC' => $obj[0]->AC,
'No_of_TW' => $obj[0]->No_of_TW,
'No' => $obj[0]->No,
'DC'=> $obj[0]->DC,
'HH_2017' => $obj[0]->HH_2017,
'FID' => $obj[0]->FID,
'Area_ha' => $obj[0]->Area_ha,
'Field_1' => $obj[0]->Field_1,
'Pop_Dens' => $obj[0]->Pop_Dens,
'Id' => $obj[0]->Id,
'Pop_2017' => $obj[0]->Pop_2017,
'Area_Sq'=> $obj[0]->Area_Sq,
));
}
}
Sample Json Format
31 => {#837
+"type": "Feature"
+"properties": {#838
+"Name": "Gujjar Pura"
+"description": null
+"altitudeMode": "clampToGround"
+"Town": "Shalimar Town"
+"AC": "31"
+"No_of_TW": "11"
+"No": "13"
+"DC": "38"
+"HH_2017": "30478"
+"FID": "31"
+"Area_ha": "648.327"
+"Field_1": "Gujjar Pura"
+"Pop_Dens": "54063.141167"
+"Id": "0"
+"Pop_2017": "196619"
+"Area_Sq": "3.63684"
}
+"geometry": {#839
+"type": "MultiPolygon"
+"coordinates": array:1 [
0 => array:1 [
0 => array:169 [ …169]
]
]
}
}
Let's support I have a model Post and I want to save additional data in JSON format in the posts table. In that case, we can use property $casts in Laravel. Which will cast our field value to whatever we gave.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table='posts';
protected $fillable = ['user_id', 'title', 'short_description', 'description', 'status', 'json_data'];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'json_data' => 'array',
];
}
Now we want to save data something like this
$data = [
'user_id' => 1,
'title' => 'abc',
'short_description' => 'test',
'description' => 'test',
'status' => true,
'json_data' => [
'additional_info' => '',
'post_image' => '',
...
],
];
$item = new Post;
$item->fill($data)->save();
This will save your json_data array values to JSON in the database. But when you will get data from the database it will convert that to array automatically.
For reference read this
since i am not a big fan of processing the json as a object
So the json_decode will accept the second arg so
$json = File::get("public/kmz/WASASubdivisions.geojson");
$data = json_decode($json,true);
dd($data);
foreach ($data as $obj)
{
Regions::create(array(
'name' => $obj['Name'],
'description' => $obj['description'],
'altitudeMode' => $obj['altitudeMode'],
'Town' => $obj['Town'],
'AC' => $obj['AC'],
'No_of_TW' => $obj['No_of_TW'],
'No' => $obj['No'],
'DC'=> $obj['DC'],
'HH_2017' => $obj['HH_2017'],
'FID' => $obj['FID'],
'Area_ha' => $obj['Area_ha'],
'Field_1' => $obj['Field_1'],
'Pop_Dens' => $obj['Pop_Dens'],
'Id' => $obj['Id'],
'Pop_2017' => $obj['Pop_2017'],
'Area_Sq'=> $obj['Area_Sq'],
));
}
Can You Post the dd() result and fileds sets of the table
public function run()
{
$json = File::get("public/kmz/WASASubdivisions.geojson");
$data = json_decode($json);
dd($data);
foreach ($data as $obj){
Regions::create(array(
'name' => $obj->Name,
'description' => $obj->description,
'altitudeMode' => $obj->altitudeMode,
'Town' => $obj->Town,
'AC' => $obj->AC,
'No_of_TW' => $obj->No_of_TW,
'No' => $obj->No,
'DC'=> $obj->DC,
'HH_2017' => $obj->HH_2017,
'FID' => $obj->FID,
'Area_ha' => $obj->Area_ha,
'Field_1' => $obj->Field_1,
'Pop_Dens' => $obj->Pop_Dens,
'Id' => $obj->Id,
'Pop_2017' => $obj->Pop_2017,
'Area_Sq'=> $obj->Area_Sq,
));
}
}
The attributes are under the properties key, but you're referencing them from the root of the object. e.g. $obj[0]->Name should be $obj[0]->properties->Name, etc.
There is a request.How I can validate it? I trying with foreach, but i does not work. Thanks.
The request:
'show_data' => [
0 => [
'buyer_search_property_id' => 1,
'date_of_show' => '2019-01-01',
'comment' => 'Nice flat',
],
1 => [
'buyer_search_property_id' => 2,
'date_of_show' => '2019-01-31',
'comment' => 'Too small',
], etc...
],
I tried this, but it does not work (of course... :))
public function rules()
{
$rules = [];
foreach ($this['show_data'] as $key => $item) {
$rules["show_data[$key]['buyer_search_property_id']"] = 'required';
$rules["show_data[$key]['date_of_show']"] = 'required|date';
$rules["show_data[$key]['comment']"] = 'required';
}
return $rules;
}
This is from laravel documentation:
You may also validate each element of an array. For example, to validate that each e-mail in a given array input field is unique, you may do the following:
$validator = Validator::make($request->all(), [
'person.*.email' => 'email|unique:users',
'person.*.first_name' => 'required_with:person.*.last_name',
]);
Read more here
You can validate array in laravel easily like this:
public function rules()
{
return [
"show_data.*.buyer_search_propery_id" => "required",
"show_data.*.date_of_show" => "required|date",
"show_data.*.comment" => "required",
];
}
if you want your method to work, do this:
public function rules()
{
$rules = [];
foreach ($this['show_data'] as $key => $item) {
$rules["show_data.$key.buyer_search_property_id"] = 'required';
$rules["show_data.key.date_of_show"] = 'required|date';
$rules["show_data.$key.comment"] = 'required';
}
return $rules;
}
just remove the inner square brackets and the quotes and add dots to the indices, so change $rules["show_data[$key]['date_of_show']"] to $rules["show_data.$key.date_of_show"]
$data['show_data'] = [
0 => [
'buyer_search_property_id' => 1,
'date_of_show' => '2019-01-01',
'comment' => 'Nice flat',
],
1 => [
'buyer_search_property_id' => 2,
'date_of_show' => '2019-01-31',
'comment' => 'Too small',
],
];
$rules = [
'show_data.*.buyer_search_property_id' => 'required',
'show_data.*.date_of_show' => 'required|date',
'show_data.*.comment' => 'required',
];
$validator = Validator::make($data, $rules);
Note asteriks at rules. With this you don't need a foreach loop. And it looks and feels a lot cleaner.
I have this query:
$data = TableRegistry::get('Dogs');
$data = $data->find('all')
->contain(['Foods'])
->select(['name','breed','sex','Foods.name','Foods.quality'])
->last()
->toArray();
It returns this result:
[
'name' => 'Dug',
'breed' => 'Golden Retriever',
'sex' => 'Male',
'food' => [
'name' => 'Best Food',
'quality' => 'A+'
]
]
I want to know if there is any way to make CakePHP return the result as:
[
'dog' => [
'name' => 'Dug',
'breed' => 'Golden Retriever',
'sex' => 'Male'
],
'food' => [
'name' => 'Best Food',
'quality' => 'A+'
]
]
How would I do this? I am pretty sure it's very simple, but I can't figure it out.
Maybe like this:
$data => [
'dog' => [
'name' => $dog->name,
'breed' => $dog->breed,
'sex' => $dog->sex
],
'food' => $dog->foods
];
$this->set('data', $data);
So following Salines' post, I decided to just build something quick and easy to work with since the content is a bit more dynamic than the example I posted.
$data = TableRegistry::get($this->table);
$data = $data->find()
->contain($associated)
->select($this->data_fields)
->last()
->toArray();
$main = strtolower(Inflector::singularize($this->table));
$data[$main] = [];
foreach ($data as $key => $value) {
if(!is_array($value)) {
$data[$main][$key] = $value;
unset($data[$key]);
}
}
i have to update a table and change the value inside it and the table have many column inside it. i tried to use query builder and here is my current code
the query is inside the public function ugetstd($id)
public function ugetstd($id) //update
{
$rules = array(
'firstname' => 'required|max:80|regex:/^[\p{L}\p{N} - ]+$/u',
'middlename' => 'required|max:50|regex:/^[\p{L}\p{N} -]+$/u', //more here
);
$messages = array(
'firstname.required' => 'First name is required',
'firstname.max' => 'First name can only contain 80 characters' //more here
);
$validator = Validator::make(Input::all(), $rules, $messages);
if ($validator->fails())
{
return Redirect::to('view_students/' . $id)
->withErrors($validator)
->withInput(Input::except('password'));
}
else
{
DB::table('dbo_students')->where('StudentID', $id )
->update
(
array
(
'FirstName' => Input::get('firstname'),
'MiddleName' => Input::get('middlename'),
'LastName' => Input::get('lastname'),
'CurrentStatusID' => Input::get('scs'),
'Sex' => Input::get('sex'),
'ReligionID' => Input::get('rel'),
'EthnicityID' => Input::get('eth'),
'StreetAddress' => $enAdd,
'CityID' => Input::get('city'),
'YearLevelID' => Input::get('yearlevel'),
'Telephone' => Input::get('telephone'),
'Birthdate' => Input::get('date'),
'Birthplace' => Input::get('birthplace'),
'SchoolLastAttended' => Input::get('schoollastattended'),
'LastGradeCompleted' => Input::get('lastgradecompleted'),
'CurrentModuleLeft' => Input::get('currentmoduleleft'),
'CurrentModuleCriticalLevel' => Input::get('modulecriticallevel'),
'StudentDescription' => Input::get('description')
)
);
return Redirect::to('view_students/' . $id);
}
}
the thing is it does not execute the code. it does not update anything and it does not throw any error. any ideas what i am doing wrong? thanks