In a URL i pass the table name with the where parameters en map this using a JSON. The problem is now is. I use DB::table($table) but this doesn't allow firstOrCreate so i wanted to try it by model, but $table::were doesn't see $table as a model name. Even if i change it from 'establisments' to 'Estalishment'. Maybe i need to put App\ before it? Because the model is not in use. How can i get the object using a model from model names as in the URL?
URL:
&spec_code=CHI&department=surgery&department_code=SUR
JSON:
{
"establishments": { // $table name, but want it to be model)
"from1": "spec_code", // from url
"to1": "abc_id" // to column in database
},
"departments": {
"from1": "department",
"to1": "name",
"from2": "department_code",
"to2": "abc_id"
},
}
Controller:
foreach(JSON as $table => $field){
if(isset($field['from2'])){
$output[$table] = DB::table($table)->where($field['to1'], $request->input($field['from1']))->where($field['to2'], $request->input($field['from2']))->first();
}elseif(isset($field['from1'])){
$output[$table] = DB::table($table)->where($field['to1'], $request->input($field['from1']))->first();
// $output[$table] = $table::where($field['to1'], $request->input($field['from1']))->firstOrCreate(); //i want this.
// $output[$table] = "\App\\".$table::where($field['to1'], $request->input($field['from1']))->firstOrCreate();
}else{
$output[$table] = null;
}
}
If someone knows how i can get it to use the model so i can firstOrCreate(), that would make my code a lot cleaner.
Kind regards,
Jeff
There are many ways to do this.
Create an array using which you can get model
$models = [
'establishments' => \App\Models\Establishment::class,
'departments' => \App\Models\Department::class,
]
Then within loop you can get the model from the array using
foreach(JSON as $table => $field){
$models[$table]::firstOrCreate(/* */)
}
As i said there are many ways to fulfill your requirement. this is one way i would do.
By Default laravel does't have out of the box method to get the Model FQN based on the table name.
Because you can have model under different namespace.
But however you can do a workaround
$tableClassToFind = 'users';
$models = [
\App\Models\User::class,
//add your model(s) here
];
$modelClass = collect($models)
->map(function($eachModelFqn){
return [
'modelClass' => $eachModelFqn,
'tableName' => app($eachModelFqn)->getTable(),
];
})
->mapWithKeys(fn($value) => [$value['tableName'] => $value['modelClass']])
->get($tableClassToFind);
dd($modelClass);
Basically it will get all the table name along with the Model FQN and changing the value of $tableClassToFind will get the appropriate Model class.
Please all the models to $models variable.
Related
I have a custom attribute that calculates the squad name (to make our frontend team lives easier).
This requires a relation to be loaded and even if the attribute is not being called/asked (this happens with spatie query builder, an allowedAppends array on the model being passed to the query builder and a GET param with the required append(s)) it still loads the relationship.
// Model
public function getSquadNameAttribute()
{
$this->loadMissing('slots');
// Note: This model's slots is guaranteed to all have the same squad name (hence the first() on slots).
$firstSlot = $this->slots->first()->loadMissing('shift.squad');
return ($firstSlot) ? $firstSlot->shift->squad->name : null;
}
// Resource
public function toArray($request)
{
return [
'id' => $this->id,
'squad_name' => $this->when(array_key_exists('squad_name', $this->resource->toArray()), $this->squad_name),
'slots' => SlotResource::collection($this->whenLoaded('slots')),
];
}
Note: squad_name does not get returned if it's not being asked in the above example, the relationship is however still being loaded regardless
A possible solution I found was to edit the resource and includes if's but this heavily reduces the readability of the code and I'm personally not a fan.
public function toArray($request)
{
$collection = [
'id' => $this->id,
'slots' => SlotResource::collection($this->whenLoaded('slots')),
];
if (array_key_exists('squad_name', $this->resource->toArray())) {
$collection['squad_name'] = $this->squad_name;
}
return $collection;
}
Is there another way to avoid the relationship being loaded if the attribute is not asked without having spam my resource with multiple if's?
The easiest and most reliable way I have found was to make a function in a helper class that checks this for me.
This way you can also customize it to your needs.
-- RequestHelper class
public static function inAppends(string $value)
{
$appends = strpos(request()->append, ',') !== false ? preg_split('/, ?/', request()->append) : [request()->append];
return in_array($value, $appends);
}
-- Resource
'squad_name' => $this->when(RequestHelper::inAppends('squad_name'), function () {
return $this->squad_name;
}),
I search for all the web and don't find a solution other than a custom function that we must make. I develops in Node.js and Laravel backend applications.
And in Node.js we send a object exactly equal to the database columns and we just use Sequelize insert function like this:
async store(model) {
try {
await this.model.city.create(model);
} catch (error) {
throw error;
}
}
We simply use the same object that sending from the app we store like this store(object); and works fine.
In Laravel I always must reference the columns even so the objects have the same attributes from the columns on database, like this:
$categoryObj = new Category;
$categoryObj->name = $category['name'];
$categoryObj->save();
return $categoryObj;
I want to just make something like this:
$categoryObj = new Category;
$categoryObj = $category;
$categoryObj->save();
return $categoryObj;
It is possible?
You can use the create method to insert a new record in the database.
class Category extends Model
{
protected $table = 'table_name';
protected $fillable = ['column_name_1', 'column_name_2', ...];
}
Then for inserting new data:
Example:
$model = YourModel::create([
'field' => 'value',
'another_field' => 'another_value'
]);
In your controller:
$categoryObj = new Category::create($category);
Also, you should look to Laravel's documentation: https://laravel.com/docs/5.8/eloquent#mass-assignment
I have a variable which holds the model name like so
$fooTableName = 'foo_defs';
$fooModel = 'FooDefs';
Now I would like to insert in the DB using that model like so
$fooModel::insert([..foo..array...]);
Throws an error
"message": "Parse error: syntax error, unexpected '$fooModel' (T_VARIABLE), expecting identifier (T_STRING)",
Is it possible to do something like that? or will I be forced to use
DB::table('fooTableName')->insert([...foo...array...]);
If I do it in the latter way, the timestamps in the table are wrong. The created_at column is null and the updated_at has the value
EDIT 1
$model = CustomHelper::getNameSpace($this->tableNames[$i]);
// $model => /var/www/html/erp/app/Models/sales/InvoiceDefs
$model::insert($this->tableCollections[$this->tableNames[$i]]);
Most of them said that, it was namespace issue, so I have corrected it, but still it is throw error like
"message": "Class '/var/www/html/erp/app/Models/sales/InvoiceDefs' not
found",
What you are doing wrong is using model name as string, you need to refactor your code as like below :
$fooModel = 'App\Models\FooDefs';
I have a same situation before and i have created the function to do this
function convertVariableToModelName($modelName='',$nameSpace='')
{
//if the given name space iin array the implode to string with \\
if (is_array($nameSpace))
{
$nameSpace = implode('\\', $nameSpace);
}
//by default laravel ships with name space App so while is $nameSpace is not passed considering the
// model namespace as App
if (empty($nameSpace) || is_null($nameSpace) || $nameSpace === "")
{
$modelNameWithNameSpace = "App".'\\'.$modelName;
}
//if you are using custom name space such as App\Models\Base\Country.php
//$namespce must be ['App','Models','Base']
if (is_array($nameSpace))
{
$modelNameWithNameSpace = $nameSpace.'\\'.$modelName;
}
//if you are passing Such as App in name space
elseif (!is_array($nameSpace) && !empty($nameSpace) && !is_null($nameSpace) && $nameSpace !== "")
{
$modelNameWithNameSpace = $nameSpace.'\\'.$modelName;
}
//if the class exist with current namespace convert to container instance.
if (class_exists($modelNameWithNameSpace))
{
// $currentModelWithNameSpace = Container::getInstance()->make($modelNameWithNameSpace);
// use Illuminate\Container\Container;
$currentModelWithNameSpace = app($modelNameWithNameSpace);
}
//else throw the class not found exception
else
{
throw new \Exception("Unable to find Model : $modelName With NameSpace $nameSpace", E_USER_ERROR);
}
return $currentModelWithNameSpace;
}
How To user it:
Arguments
First Argument => Name of the Model
Second Argument => Namespcce of the Model
For Example we have the model name as Post
$postModel = convertVariableToModelName('Post');
dd($postModel::all());
Will returns all the values in the posts table
But in Some Situation You Model Will in the
Custom Namespace such as App\Models\Admin\User
So this function is created to overcome that
$userModel = convertVariableToModelName('User',['App','Models','Admin']);
dd($userModel::all());
You are feel free to customize the function
Hope it helps
Try the below one,
$fooModel = new FooDefs();
and then you can do the following also,
$fooModel->column1 = $value1;
$fooModel->column2 = $value2;
$fooModel->column2 = $value2;
$fooModel->save();
or
$fooModel->save([
'column1' => $value1,
'column2' => $value2,
'column3' => $value3,
])
Edited answer
$path = 'my\project\path\to\Models';
$fooModel = app($path.'\FooDefs');
$fooModel::save([
'column1' => $value1,
'column2' => $value2,
'column3' => $value3,
])
dd($fooModel ::all());
Try my edited answer.
When you have the name of class stored as string you can call method on that class by using the php method call_user_func_array like this
$class_name = "FooDefs";
call_user_func_array(array($class_name, "insert"), $data);
$data is an Array of data which will be past to the called function as arguments.
Just for simple advice It's will be Good if you save in the $class_name variable the FQN Fully Qualified Name of the class which is the __NAMESPACE__ follow by the name of the class. For sample purpose FQN look like Illuminate\Support\Facades\DB which you can get when you save you use User::class I presume you have some User model. That will return the Fully Qualified Name of the User model will will be App\User in case of Laravel.
$requests = $post['request'] // posting the data from view page
$models = "app\models".'\\'.$requests //geting the model
$model = $models::findOne($referenceId) //fetching value from database
we try to update the json data key value.we stored record in database product_name column like
{"ar":"arabic product","en":""}
we try to achieve this both below two method
DB::table('products')
->where('id', 1)
->update(['productname->en' => 'english product']);
$post = product::find($product_id);
$post->product_name = [$language => $edittitle];
$post->save();
we want to update the json en key value.
A quick and easy solution to update all the entries in one go:
Product::each(function ($product) {
$productName = json_decode($product->product_name, true);
$productName['en'] = 'english product';
$product->product_name = json_encode($productName);
$product->save();
});
If you want to update a single product, this should work:
$product = Product::find($product_id);
$productName = json_decode($product->product_name, true);
$productName['en'] = 'english product';
$product->product_name = json_encode($productName);
$product->save();
First thing is to create that field with the right type in the migration file, the keyword we need there is json:
...
$table->json('productname');
...
Then we need to cast that attribute to the right type when retrieving it as Eloquent model by using $casts attribute on the model itself:
...
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'productname' => 'array'
];
...
Then we can access and save data for this attribute like so:
$myModel = product::find(1);
$myModel->productname['en'] = 'something';
// or
$myModel->productname = ['en' => 'something']; // This will override the whole attribute
For further details you can take a look at: https://laravel.com/docs/5.7/eloquent-mutators#attribute-casting
I'm using Laravel 5.7 and have a one-to-one relationship between 2 eloquent models.
I have this simple function that works well, and the correct values persist to the database:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
return $contact->marketingOrigin;
}
However, when writing a functional test for it, I noticed that the object that it returns is stale (doesn't have the correct values in its properties).
My tests only pass if I change the return statement to return \App\Models\MarketingOrigin::find($contact->id);.
(MarketingOrigin uses 'contact_id' as primary key.)
What am I doing wrong?
How can I return the same object that was just saved in the previous line ($contact->marketingOrigin->save();) without making a database read query (find())?
Update to respond to comments:
protected $table = 'marketing_origins';//MarketingOrigin class
protected $primaryKey = 'contact_id';
protected $guarded = [];
public function contact() {
return $this->belongsTo('App\Models\Contact');
}
The test:
public function testSaveMarketingOriginInfo() {
$helper = new \App\Helpers\SignupHelper();
$contactId = 92934;
$contact = factory(\App\Models\Contact::class)->create(['id' => $contactId]);
$leadMagnetType = 'LMT';
$audience = 'a60907';
$hiddenMktgFields = [
'audience' => $audience,
'leadMagnetType' => $leadMagnetType
];
$result = $helper->saveMarketingOriginInfo($contact, $hiddenMktgFields);
$this->assertEquals($result->contact_id, $contactId, 'contact_id did not get saved');
$this->assertEquals($result->campaignId, '6075626793661');
$this->assertEquals($result->leadMagnetType, $leadMagnetType);
$marketingOrigin = \App\Models\MarketingOrigin::findOrFail($contactId);
$this->assertEquals($marketingOrigin->adsetId, '6088011244061');
$this->assertEquals($marketingOrigin->audience, $audience);
$this->assertEquals($marketingOrigin, $result, 'This is the assertion that fails; some properties of the object are stale');
}
This is because the relationship has not been loaded yet.
You could try $contact->load('marketingOrigin'); to eager load the relationship:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
$contact->load('marketingOrigin'); // <---- eager load the relationship
return $contact->marketingOrigin;
}