access to create array in laravel Mutator - php

I want to create a new user by calling create method like this:
User::create([
'phone_number' => '09121231212',
'country_code' => 'ir',
]);
I want to change the phone number format to international phone number format by Propaganistas\LaravelPhone package in phone number mutator like this:
public function phoneNumber(): Attribute
{
return Attribute::make(
get: fn ($value) => (new PhoneNumber($value, $this->country_code))->formatNational(),
set: fn ($value) => (new PhoneNumber($value, $this->country_code))->formatE164(),
);
}
the problem is that in the phone_number mutator(set) I don't have access to the country_code that is defined in create array so I can not change the phone_number format before inserting it into the database.
also, I don't want to merge country_code to request and get it in the mutator. is there any better solution?

As per the documentation, you can access other attributes in the getter by adding a second parameter to the closure:
public function phoneNumber(): Attribute
{
return Attribute::make(
get: fn ($value, $attributes) => (new PhoneNumber($value, $attributes['country_code']))->formatNational(),
);
}
It appears you'll only able to access other attributes from the models in a custom cast:
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class PhoneNumberCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return (new PhoneNumber($value, $attributes['country_code']))
->formatNational();
}
public function set($model, $key, $value, $attributes)
{
return (new PhoneNumber($value, $attributes['country_code']))
->formatE164();
}
}

Related

Laravel access to column object

I have a table, table name is bookings and here have a column e_provider. this column i direct fetch data by id and save all data in e_provider field
how can i access this e_provider data like $data->e_provider->name
here is code
[
{
"id": 2,
"e_provider": "{"id":11,"name":"Architect O'Reilly, Ratke and Miller","phone_number":"661.425.3559","mobile_number":"307.607.7472"}",
}
]
in laravel 8 and less try this
Defining A Accessors & Mutators
public function getEProviderAttribute(){
return json_decode($this->getAttributeValue('e_provider'));
}
public function setEProviderAttribute($value){
return json_encode($value);
}
For update value
$e_provider = $object->e_provider;
$e_provider->name = "new name";
$object->e_provider = $e_provider;
$object->save();
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
You can use $casts in your model
class BookingModel extends Model
{
protected $casts = [
'e_provider' => AsCollection::class,
];
}
Now you will be able to get the data by the following:
$bookingModel->e_provider->id
https://laravel.com/docs/9.x/eloquent-mutators#array-object-and-collection-casting
Another way if you don't want to use casts, you can define an accessor:
protected function eProvider(): Attribute
{
return Attribute::make(
get: fn ($value) => json_decode($value),
set: fn ($value) => json_encode($value),
);
}
https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor

Laravel - Shared data reduce number of queries

I am trying to make a global search feature on my website, and for that I need to query the database to get the records. The frontend of my application expect the following array:
[
['name' => 'Result #1...', 'url' => 'http://...'],
['name' => 'Result #2...', 'url' => 'http://...'],
['name' => 'Result #3...', 'url' => 'http://...'],
]
To accomplish this, I have added the below in my AppServiceProvider:
public function boot()
{
view()->composer('*', function($view) use ($auth) {
return \View::share('SearchData', (new GlobalSearch($auth->user()->currentTeam))->all());
});
}
The $SearchData is created in the GlobalSearch class:
class GlobalSearch
{
public $data;
public function __construct(public Team $team){
$this->data = $team->properties()->with(['leases', 'leases.tenant', 'leases.files', 'leases.invoices']);
}
protected function propertyData() : array
{
$properties = $this->data->get();
return $properties->map(function ($property) {
$array['name'] = \Str::limit($property->address, 40);
$array['url'] = route('properties.show', ['property' => $property]);
return $array;
})->toArray();
}
public function all() : array
{
return $this->propertyData();
}
}
Now the above code does work - I successfully get an array in the correct mapping. However, in my database I only have 1 property in the properties table - yet, there are being executed 90 duplicate queries for a single page load.
Why is this happening? I can't seem to locate why these queries are being duplicated
You can actually remove the view()->composer('*', function) part. Since View::share() does the exact same.
$searchData = (new GlobalSearch($auth->user()->currentTeam))->all();
View::share('SearchData', $searchData);
Optionally:
You could bind the GlobalSearch class to the container in your AppServiceProvider. Which will make the class available via app(GlobalSearch::class) wherever you want in the application. This means the query will only run once during the initialization process and maintain the data.
More info about singleton bindings: https://laravel.com/docs/8.x/container#binding-a-singleton
$this->app->singleton(GlobalSearch::class, function ($app) {
return new GlobalSearch(auth()->user()->team);
});

Possible to add multiple parameters to excel import?

I have a basic form that contains an excel file upload and a select option.
The select contains a few options. I want to pass the selected option from the user with the file to my Excel::import
I tried passing another parameter to my import but it returns error. (has to be string, array given)
Is this possible using Laravel excel import?
Controller
public function create(Request $request)
{
if($request->hasFile('file'))
{
$package = $request->input('package');
// how can I add $package to my import to insert it to my user model?
Excel::import(new AccountsImport, $request->file('file'));
return ['message' => 'Excel has been succesfully uploaded'];
}
}
AccountsImport
class AccountsImport implements ToCollection, withHeadingRow
{
public function collection(Collection $rows)
{
$rows->each(function($row, $key) {
Account::create([
'name' => $row['student'],
'email' => $row['e_mail'],
// Want it to be possible to add my package here
//'package' => $package
]);
});
}
}
Maybe you could pass down custom data to your AccountsImport class?
You would have a data array as such:
$data = [
'package' => $package,
// other data here
];
You would then do something like this:
Excel::import(new AccountsImport($data), $request->file('file'));
That would require some changes in your import class.
class AccountsImport implements ToCollection, withHeadingRow
{
private $data;
public function __construct(array $data = [])
{
$this->data = $data;
}
public function collection(Collection $rows)
{
$rows->each(function($row, $key) {
Account::create(array_merge([
'name' => $row['student'],
'email' => $row['e_mail'],
// Want it to be possible to add my package here
//'package' => $package
], $this->data));
});
}
}
I took a look at Laravel Excel's API and couldn't see something that would cater for this, but this should work.

Eloquent all and custom property

Let's say, I have an Order model and I have an endpoint, which querys all the orders.
Now it's something like this:
public function findAll(Request $request, Response $response, $args)
{
return Order::all()->toJson(JSON_PRETTY_PRINT);
}
But, I want to add custom properties to the json response.
My order looks like this:
protected $fillable = [
'productIds',
'fullname',
'phone',
'location'
];
And I want to add a 'names' and a 'totalPrice' property, which are not in the DB, they are calculated by a getProductNameList() and a getSumPrice() method. How is that possible? :)
You could try to add the data directly from the model using the $append model attribute like this:
Class Order extends Model
{
protected $append = ['attribute_name'];
public function getAttributeName() {
$dataToReturn = [];
return ($dataToReturn);
}
}
Doc here : https://laravel.com/docs/5.4/eloquent-serialization#appending-values-to-json
you can use json response so you can pass custom array
public function findAll(Request $request, Response $response, $args)
{
$order= Order::all();
return response()->json([
'order' => $order,
'names' => 'CA',
'totalPrice'=>150
]);
}
As per your question you are using separate methods for calculating so getProductNameList() and a getSumPrice() .In both method you can return $value and method response you can pass it to this array
For more ref:
https://laravel.com/docs/5.5/responses#json-responses

Laravel 5.2 - Using SetIdAttribute() Mutator To Set Other Value

I am currently creating a blog where each Post row in my database will have a unique hash attribute that is based of the post's id (incrementing, always unique).
This my Post model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Hashids;
class Post extends Model
{
public function setTitleAttribute($value)
{
$this->attributes['title'] = $value;
if (! $this->exists) {
$this->attributes['slug'] = str_slug($value);
}
}
public function setIdAttribute($value) {
$this->attributes['id'] = $value;
$this->attributes['hash'] = Hashids::encode($value);
}
}
When I run this factory
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'title' => $faker->sentence(mt_rand(3, 10)),
'content' => join("\n\n", $faker->paragraphs(mt_rand(3, 6))),
'author' => $faker->name,
'category' => rand(1, 20),
];
});
The setIdAttribute($value) function is getting called, but my hash attribute is not being set. I am not sure if it is getting overwritten or what.
If I move the line
$this->attributes['hash'] = Hashids::encode($value);
to the function
public function setTitleAttribute($value)
and encode the title attribute it works fine, but I want to encode the 'id' attribute. Any idea how I would do this?
You can add the following to your model:
/**
* Events
*/
public static function boot()
{
parent::boot();
static::created(function($model)
{
$model->hash = Hashids::encode($model->id);
$model->slug = str_slug($model->title);
}
}
It's likely setIdAttribute($value) isn't being called until after the insert runs because it doesn't know the ID until then.
The real issue is you can't set a hash of the id in the same query because the id isn't going to be known (assuming it's auto_incrementing) until after the insert.
Because of this, the best you can probably do here is fire some code on the model's saved event.
In that model, you can probably do something like...
public static function boot()
{
parent::boot();
static::flushEventListeners(); // Without this I think we have an infinite loop
static::saved(function($post) {
$post->hash = Hashids:encode($post->id);
$post->save();
});
}

Categories