yii2 get getter's value in active query request (using asArray()) - activerecord

I have the next code:
$fixed_events = EventMain::find()
->select(["id", "title", "files"])
//->joinWith(['files'])
//->with(['files'])
->asArray()
->all();
How can i get array with "files" value, taking into account, that the "files" is modle's getter like
public function getFiles()
{
return (json_decode($this->all_files, true)) ?: [];
}

Since files is not a relation with EventMain table, I guess the easiest approach would be handle the data and convert with ArrayHelper after coming from db:
<?php
use yii\helpers\ArrayHelper;
$models = EventMain::find()->select(['id', 'title'])->all();
$array = ArrayHelper::toArray($models, [
'app\models\EventMain' => ['id', 'title','files']
]);
var_dump($array);
?>

Related

CodeIgniter 3 code does not add data to database into 2 different tables (user_info & phone_info)

The problem is when I entered a new name no data is added. A similar thing happen when I entered an already existing name. Still, no data is added to the database. I am still new to CodeIgniter and not entirely sure my query builder inside the model is correct or not.
In the Model, I check if the name already exists insert data only into the phone_info table. IF name does not exist I insert data into user_info and phone_info.
Controller:
public function addData()
{
$name = $this->input->post('name');
$contact_num = $this->input->post('contact_num');
if($name == '') {
$result['message'] = "Please enter contact name";
} elseif($contact_num == '') {
$result['message'] = "Please enter contact number";
} else {
$result['message'] = "";
$data = array(
'name' => $name,
'contact_num' => $contact_num
);
$this->m->addData($data);
}
echo json_encode($result);
}
Model:
public function addData($data)
{
if(mysqli_num_rows($data['name']) > 0) {
$user = $this->db->get_where('user_info', array('name' => $data['name']))->result_array();
$user_id = $user['id'];
$phone_info = array(
'contact_num' => $data['contact_num'],
'user_id' => $user_id
);
$this->db->insert('phone_info',$phone_info);
} else {
$user_info = array(
'name' => $data['name']
);
$this->db->insert('user_info', $user_info);
$user = $this->db->get_where('user_info', array('name' => $data['name']))->result_array();
$user_id = $user['id'];
$phone_info = array(
'contact_num' => $data['contact_num'],
'user_id' => $user_id
);
$this->db->insert('phone_info', $phone_info);
}
}
DB-Table user_info:
DB-Table phone_info:
Extend and change your model to this:
public function findByTitle($name)
{
$this->db->where('name', $name);
return $this->result();
}
public function addData($data)
{
if(count($this->findByTitle($data['name'])) > 0) {
//.. your code
} else {
//.. your code
}
}
Explanation:
This:
if(mysqli_num_rows($data['name']) > 0)
..is not working to find database entries by name. To do this you can use codeigniters built in model functions and benefit from the MVC Pattern features, that CodeIgniter comes with.
I wrapped the actual findByName in a function so you can adapt this to other logic and use it elswehere later on. This function uses the query() method.
Read more about CodeIgniters Model Queries in the documentation.
Sidenote: mysqli_num_rows is used to iterate find results recieved by mysqli_query. This is very basic sql querying and you do not need that in a MVC-Framework like CodeIgniter. If you every appear to need write a manual sql-query, even then you should use CodeIgniters RawQuery methods.

Laravel Mutator for append url for JSON array

I have a JSON field in my MySQL table column which has an JSON array with part of URLs.
["products/1.jpg", "products/2.jpg", "products/3.jpg"]
I want to get the array with appending a Base URL for each of the values of the array.
["www.example.com/images/products/1.jpg", "www.example.com/images/products/2.jpg", "www.example.com/images/products/3.jpg"]
I have tried with getAttribute() function like nelow code. But was not succeeded.
public function getImagesAttribute(){
$images = json_decode($this->attributes['images']);
$imageP = [];
foreach ($images as $image) {
$imageP[] = "www.example.com/images/" . $image;
}
return $imageP;
}
can you help me.
You probably want to use $this->images instead of $this->attributes['images'].
In this case, I would use Collections like so:
public function getImagesAttribute(){
return collect(json_decode($this->images))
->map(function ($image) {
return "www.example.com/images/" . $image;
})
->all();
}

How can I convert json to a Laravel Eloquent Model?

if I have an Eloquent Model called Post, and the mysql table has:
integer ID,
string Text
How do I convert this JSon:
{ post: { text: 'my text' } }
To the relevant Post object that, once received in the controller, I can save to the database like this:
public function store(Post $post)
{
$post->save();
}
I'm not looking to build the logic that would do that for me, but for the Laravel way (or could it be that there isn't one? I googled it with no relevant results).
Convert json to array
Hydrate model from array
$data = '{
"unique_id_001":{"name":"John","email":"JD#stackoverflow.com"},
"unique_id_002":{"name":"Ken","email":"Ken#stackoverflow.com"}
}';
$object = (array)json_decode($data);
$collection = \App\User::hydrate($object);
$collection = $collection->flatten(); // get rid of unique_id_XXX
/*
Collection {#236 ▼
#items: array:2 [▼
0 => User {#239 ▶}
1 => User {#240 ▶}
]
}
*/
dd($collection);
fill looks like the method you want. To avoid adding every attribute to your $filled array, which you would need to do if you wanted to use the fill method, you can use the forceFill method.
It accepts an associative array of attributes, so the JSON will have to be decoded, and we'll have to get the inner post key:
$rawJson = "{ post: { text: 'my text' } }";
$decodedAsArray = json_decode($rawJson, true);
$innerPost = $decodedAsArray['post'];
Once we have the decoded data, we can create an instance of the Post eloquent model and call forceFill on it:
$post = new Post();
$post->forceFill($innerPost);
$post->save();
This is similar to doing:
$post = new Post();
foreach ($innerPost as $key => $value) {
$post->$key = $value;
}
$post->save();
Just turn it to array and fill an eloquent
$arr = json_decode($json, true);
$post = new Post;
$post->fill($arr);
It's way simple as like followings:
$json_post = { "post": { "text": "my text" } };
$post = new Post(
json_decode($json_post, true)
);
Now, you can run all eloquent methods on the most $post, ex:
$post->save()
I tested with laravel v7.11.0
Can you try it like this?
public function store($poststuff)
{
$post = new Post;
$post->text = $poststuff['text'];
$post->save();
}

Laravel Excel - Select columns to export

I'm using Laravel Excel from maatwebsite but i have a problem with export data to xls file. I have this query but i don't need to show all columns in the xls file but i need to select all this columns to do operations before download the file. In my select i have 8 columns but in my headers i just have 7 to show but this doesn't work because the 8ª column appears too.
MY FUNCTION:
public function toExcel($id) { $b = Budget::find($id);
$budget = Budget_Item::join('budgets', 'budget__items.id_budget', '=', 'budgets.id')
->join('items_sizes', 'budget__items.id_itemSize', '=', 'items_sizes.id')
->join('items', 'items_sizes.id_item', '=', 'items.id')
->join('material_types', 'items_sizes.id_materialType', '=', 'material_types.id')
->select('items.reference AS Referência', 'items.name AS Descrição', 'items_sizes.size AS Tamanho', 'material_types.material_type AS Material', 'budget__items.amount AS Quantidade', 'items_sizes.unit_price AS Val.Unitário', 'budget__items.price AS Val.Total', 'budget__items.purchasePrice')
->where('id_budget', '=', $id)
->get();
$budgetUpdate = [];
$budgetUpdate[] = ['Referência', 'Descrição', 'Tamanho', 'Material', 'Quantidade', 'Val.Unitário', 'Val.Total'];
foreach ($budget as $key)
{
if ($key->purchasePrice > 0)
{
$key->unit_price = $key->purchasePrice;
}
$budgetUpdated[] = $key->toArray();
}
Excel::create('Proposta_'.$b->reference, function($excel) use($budgetUpdated)
{
// Set the title
$excel->setTitle('Proposta');
$excel->sheet('Sheetname', function($sheet) use($budgetUpdated)
{
$sheet->fromArray($budgetUpdated, null, 'A1', false, true);
});
})->download('xls');}
How can i solve that?
Thank's
Tested on Laravel excel 3.1 docs
on controller
public function exportAsCsv()
{
return Excel::download(new MyExport, 'invoices.xlsx');
}
on MyExport, look at the map function
class MyExport implements FromCollection, WithHeadings, WithMapping{
public function collection(){
return MyTable:all();
}
// here you select the row that you want in the file
public function map($row): array{
$fields = [
$row->myfield2,
$row->myfield1,
];
return fields;
}
}
also check this
For what its worth I ran into a similar issue and didnt find much in terms of a fix so heres my solution.
1. I query the DB in the callback and get all the data I need.
2. I then go over the collection and create a new array on each iteration and assign a value & header. Works great for picking out columns from a model
Excel::create('User List Export', function($excel) {
$excel->sheet('Users', function($sheet) {
$email = yourModelGoesHere::all();
foreach ($email as $key => $value) {
$payload[] = array('email' => $value['email'], 'name' => $value['name']);
}
$sheet->fromArray($payload);
});
})->download('xls');
You can try this way.
$user_id = Auth::user()->id
Excel::create('User', function($excel) use ($user_id){
$excel->sheet('Sheet', function($sheet) use($user_id){
$user = User::where('user_id', $user_id)->select('name', 'email', 'role', 'address')->get();
});
})->export('xls');
return redirect('your route');

Validate single rows only when they are present Laravel

So i have a model named Customer.
The db the Customer looks like this:
id, name, lastName, personal, address, zip, location, phones, emails updated_at, created_at
Emails and Phones is special rows because they are store as an json object example
['john#doe.com', 'some#othermail.com', 'more#mails.com']
I use the Customer Model to store the validation rules and custom messages like this
<?php
class Customer extends BaseModel
{
public function validationRules()
{
return array(
'name' => 'required|max:255',
'lastName' =>'max:255',
'personal'=> 'integer',
'location' => 'max:255',
'address' => 'max:255',
'zip' => 'required|integer',
'phones' => 'betweenOrArray:8,10|required_without:emails',
'emails' => 'emailOrArray'
);
}
public function validationMessages()
{
// returns Validation Messages (its too much to write down)
}
}
The OrArray Rule is found here https://stackoverflow.com/a/18163546/1430587
I call them through my controller like this
public function store()
{
$customer = new Customer;
$messages = $customer->validationMessages();
$rules = $customer->validationRules();
$input['name'] = Input::get('name');
$input['lastName'] = Input::get('lastName');
$input['personal'] = preg_replace("/[^0-9]/", "", Input::get('personal'));
$input['location'] = Input::get('location');
$input['address'] = Input::get('address');
$input['zip'] = Input::get('zip');
$input['emails'] = Input::get('emails');
$input['phones'] = Input::get('phones');
foreach($input['phones'] as $i => $value)
{
$input['phones'][$i] = preg_replace("/[^0-9]/", "", $value);
}
$validator = Validator::make($input, $rules, $messages);
}
This all works just fine, but I want to be able to PUT/PATCH request to update a single row.
But the validationRules has Required on certain fields so when its not present i cant update that single row. Without getting an error that the other fields (witch I'm not posting) is required.
How would I best approach this ?
You should get that instance of the model that represent the row you want to edit, that's why the resource controller's update method has a parameter that is the resource you want to edit.
public function update($resourceId) {
$customer = Customer::where('id', '=', $resourceId);
}
Now this customer has all the attributes you set before, so you can access them like:
$customer->name;
$customer->lastName;
So when you valide the values you can use the existing values in your validator where the input is empty:
$input['name'] = (Input::get('name')) ? (Input::get('name')) : $customer->name;
Or a prettier solution with the elvis operator:
$input['name'] = (Input::get('name')) ?: $customer->name;
I came up with another solution to this problem that works very well and its much cleaner.
$customer = Customer::find($id);
$input = Input::except('_method', '_token');
$customer->fill($input);

Resources