How to save data on different database tables in Laravel Backpack - laravel

how to save the data from the Features fields on a different table. Example in the link below, it is saving the Features table fields in a JSON field in the database. However, I want to save this data from the features into another table.
https://demo.backpackforelavel.com/admin/product/211/Edit
I'm coming back here to post my answer. I managed to settle, I'm putting here to help other developers.
This first part of the question I have already solved. But now I can not bring the data from the Features fields in the form.
Below is the source code that I was able to save and edit the form data. However, I can not carry the data from the Feature fields. Someone knows how I can carry the field data in Feature
class ProductCrudController extends CrudController
{
use ListOperation;
use CreateOperation {
store as traitStore;
}
use UpdateOperation {
update as traitUpdate;
}
use DeleteOperation;
public function store()
{
// insert item in the db
$item = $this->crud->create($this->crud->getRequest()->except(['save_action', '_token', '_method']));
$features = json_decode($this->crud->getRequest()->input('features'));
foreach ($features as $itens) {
$obj = new Feature();
$obj->product_id = $item->getKey();
$obj->name = $itens->name;
$obj->save();
}
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
return redirect('admin/product');
}
public function update()
{
$featureForm = json_decode($this->crud->getRequest()->input('features'));
// insert item in the db
$item = $this->crud->update($this->crud->getRequest()->id, $this->crud->getRequest()->except(['save_action', '_token', '_method', 'features']));
$objF = Feature::where('product_id', $item->getKey())->get();
foreach ($objF as $itens) {
Feature::where('id', $itens->id)->delete();
}
foreach ($featureForm as $itens) {
$obj = new Feature;
$obj->product_id = $item->getKey();
$obj->name = $itens->name;
$obj->save();
}
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
return redirect('admin/product');
}
}
I'm coming back here to post my answer. I managed to settle, I'm putting here to help other developers.
I was able to bring the data in the form edition, in the Features fields. I used the Mutators in the Model - https://laravel.com/docs/8.x/eloquent-mutators
Below is the example I did.
It would be interesting to have an example of this in the official documentation or demo project. I believe it would help a lot also to other developers.
//model Product
public function getFeaturesAttribute($value)
{
$objects = Features::where('product_id', $this->id)->get();
$array = [];
foreach ($objects as $itens) {
$obj = new stdClass;
$obj->name = $itens->name;
$array[] = $obj;
}
return \json_encode($array);
}

There are a few ways to go about it. Personally I prefer to create accessors and mutators in the model - https://laravel.com/docs/8.x/eloquent-mutators . Not only does it keep the CrudController clean (so updateable) but it also allows you to save/load stuff from other forms (ex: user-facing forms).
//model Product
public function getFeaturesAttribute($value)
{
$objects = Features::where('product_id', $this->id)->get();
$array = [];
foreach ($objects as $itens) {
$obj = new stdClass;
$obj->name = $itens->name;
$array[] = $obj;
}
return json_encode($array);
}

Related

Update Field After Save in Laravel

I have a questions database table with the field featured_image and my questions model has an attachOne file named featured_image_file. The featured_image_file column doesn't get saved to the database, so for my API, I am updating the field featured_image after save.
I saw some answers to this question on other posts that didn't work. Eventually, I figured it out and my resulting code is this:
public function save(?array $options = [], $sessionKey = null)
{
$user = BackendAuth::getUser();
$this->user_id = $user->id;
// throw new ApplicationException(json_encode($this));
parent::save($options);
$url = $this->featured_image_file->getPath();
$this->featured_image = $url;
parent::save($options);
}
Is this the proper method to modify when inserting into the database? Is it okay to call parent::save($options) twice?

Stuck at Error = Method Illuminate\Database\Eloquent\Collection::save does not exist

Trying to save data while open page but stuck at error :
"Method Illuminate\Database\Eloquent\Collection::save does not exist."
I have 2 database :
Buffalodata
Buffalomilkrecord
From 2nd table i need to get the avg of totalmilk and update the same to main database (1). This help me to show updated avgmilk data on dashboard front page.
Route:
Route:: get('buffalo-details', 'App\Http\Controllers\BuffalodataController#buffalodetails');
BuffalodataController Controller :
public function buffalodetails()
{
$buffalidforavgmilk = Buffalodata::groupBy('buffaloID')->get('buffaloID')->pluck('buffaloID')->toArray();
foreach ($buffalidforavgmilk as $id )
{
$milkperid = Buffalomilkrecord::where('buffaloID', $id)->sum('totalmilk');
$avgbuffalocount = Buffalomilkrecord::where('buffaloID',$id)->count();
$getavg = $milkperid / $avgbuffalocount;
$data = Buffalodata::find($buffalidforavgmilk);
$data->avgmilk = ($getavg);
$data->save ();
// dump([$milkperid,$avgbuffalocount,$getavg,$data,$id]);
}
return view ('pages.Buffalo.BuffaloDetails',[---------]);
}
Thanks again in Advance
When you pass an Array to ::find(), it returns a Collection, which doesn't have a save() method. This is your code:
// This is an Array of `buffaloID` values
$buffalidforavgmilk = Buffalodata::groupBy('buffaloID')->get('buffaloID')->pluck('buffaloID')->toArray();
...
// `$data` is now a `Collection` of `Buffalodata` instances
$data = Buffalodata::find($buffalidforavgmilk);
// This now fails, as `Collection` doesn't have a `save()` method
$data->save();
You can rewrite your code as follows:
Buffalodata::whereIn('buffaloID', $buffalidforavgmilk)->update(['avgmilk' => $getavg]);
This will update all records in a single call. If you want to iterate, that's an option too:
$data = Buffalodata::find($buffalidforavgmilk);
foreach ($data as $record) {
$record->avgmilk = $getavg;
$record->save();
}
Or, since you have $id already:
$record = Buffalodata::find($id);
$record->avgmilk = $getavg;
$record->save();

How to insert array data in laravel 5.4

I have a product table and device table.
I have joined these two tables where I have selected device.id & product.name, now I want to insert this data into my existing device table using device.id.
I want to insert multiple dropdown values in my database. Can anyone tell me how can I solve this problem? Below is my Controller Code-
foreach ($request->all() as $value)
{
$deviceById= Device::find($request->id);
if($request->destination_type == 'dealer')
{
$deviceById->destination_location=$value->dealer_id;
}else
{
$deviceById->destination_location=$value->office_id;
}
$deviceById->save();
}
flash('Device Dispatch added successfully!')->success()->important();
return redirect()->back();
You can make array of all dropdowns value and convert into a json string and store it in database
You can do something like this. Change according to your requirement. It just a logic demo.
$deviceById= Device::find($request->id);
$destination = array();
foreach ($request->all() as $value)
{
if($request->destination_type == 'dealer')
{
$destination[] = $value->dealer_id;
}else
{
$destination[] = $value->office_id;
}
}
$jsonString = json_encode($destination);
$deviceById->destination_location = $jsonString
$deviceById->save();
flash('Device Dispatch added successfully!')->success()->important();
return redirect()->back();
I solve it this way.I had used Elequent ORM
if($request->destination_type == 'Dealer')
{
DB::table('devices')
->whereIn('id',$request->device_list)
->update(['dealer_id'=>$request->destination_location,'device_status'=>"Dispatch"]);
flash('Device Dispatch dealer added successfully!')->success()->important();
}else
{
DB::table('devices')
->whereIn('id',$request->device_list)
->update(['office_id'=>$request->destination_location,'device_status'=>"Dispatch"]);
flash('Device Dispatch office added successfully!')->success()->important();
}

Retrieve parent class within morph relationship

I have this code
//ImageableTrait
trait ImageableTrait
{
public function images()
{
return $this->morphMany(Image::class, 'imageable')
->orderBy('order', 'ASC');
}
}
//User
class User extend Model
{
use ImageableTrait;
}
//Post
class Post extend Model
{
use ImageableTrait;
}
class ImageCollection extends Collection
{
public function firstOrDefault()
{
if ($this->count() === 0) {
$image = new Image();
$image->id = 'default';
$image->imageable_type = '/* I need the parent className here */';
$image->imageable_id = '.';
}
return $this->first();
}
}
//Image
class Image extend Model
{
public function imageable
{
return $this->morphTo();
}
public function newCollection(array $models = [])
{
return new ImageCollection($models);
}
public function path($size)
{
//Here, there is some logic to build the image path and it needs
//the imageable_type attribute no matter if there is
//an image record in the database or not
return;
}
}
I want to be able to do so
$path = User::find($id)->images->firstOrDefault()->path('large');
But I can't figure out how to get the parent class name to build the path properly...
I tried with $morphClass or getMorphClass() but can't figure out how to use it properly or if it is even the right way to do it.
Any thoughts on that?
I think you can keep it simple and drop the ImageCollection class because there is already a firstOrNew method that seems to be what you're looking for.
The firstOrNew method accepts an array of attributes that you want to match. If you don't care about the attributes, you can pass an empty array. If there are no matches in the database, it'll make a new instance with the proper parent type.
$path = User::find($id)->images()->firstOrNew([])->path('large');
Note: I am calling the images() method to get the MorphMany object so that I can call the firstOrNew method. In other words, you need to add the parentheses. Otherwise, you get a Collection.
Edit: If you want to make things a bit simpler by automatically setting some default attributes, you can add this to your ImageableTrait:
public function imagesOrDefault()
{
$defaultAttributes = ['id' => 'default'];
return $this->images()->firstOrNew($defaultAttributes);
}
Then, you can do something like this: $path = User::find($id)->imagesOrDefault()->path('large');
Note that your default attributes must be fillable for this to work. Also, imageable_id and imageable_type will automatically be set to your parent's id and type.
If you want to set the default value for imageable_id to a period and not the parent's id, then you have to alter it a bit, and it will look a lot like your original code except this will go inside your ImageableTrait.
public function imagesOrDefault()
{
// First only gets one image.
// If you want to get all images, then change it to get.
// But if you do that, change the conditional check to a count.
$image = $this->images()->first();
if (is_null($image))
{
$image = new Image();
$image->id = 'default';
$image->imageable_type = $this->getMorphClass();
$image->imageable_id = '.';
}
return $image;
}
Ok guys I've found something that seems to work pretty good for now so I'll stick with that.
In the Image model, I've added some code when I make the new collection:
public function newCollection(array $models = [])
{
$morphClass = '';
$parent = debug_backtrace(false, 2)[1];
if (isset($parent['function']) AND $parent['function'] === 'initRelation') {
if (isset($parent['args'][0][0])) {
$morphClass = get_class($parent['args'][0][0]);
}
}
return new ImageCollection($models, $morphClass);
}
I then simply retrieve the morphClass through the constructor of the ImageCollection
private $morphClass;
public function __construct($items = [], $morphClass)
{
parent::__construct($items);
$this->morphClass = $morphClass;
}
public function firstOrDefault()
{
if ($this->count() === 0) {
$image = new Image();
$image->id = 'default';
$image->imageable_type = $this->morphClass;
$image->imageable_id = '.';
}
return $this->first();
}
This way, I can simply call the method like that
User::with('images')->get()->images->firstOrDefault()
This seems to work really great in many cases, if I have some issues at some times, I'll update this post.
i may be late for the party, but i kinda did a small trick for morph relationships where i had "media" as morph, i get the parent since "model_type" has the full string parent class string.
$model = new $media->model_type;
$media->model = $model->findOrFail($media->model_id);

Object Oriented Approach in Codeigniter Model

I have been wondering what is the right way to write code in OO style in model. Certainly you can have a function that retrieve data from DB and then map to model-level variables. This approach, however, becomes counterintuitive when you have other function in model trying to get other data from BD. For example:
class User extends CI_Model {
$id, $name, $age .... ;
public function get_user_from_db_with_id($id) {
...
// get data and map to variable.
}
public function get_all_users() {
// return all users in db
}
}
somewhere in controller:
$user = new User();
$ben = $user->get_user_from_db_with_id($id);
// this does not make sense!!
$all_user = $ben->get_all_users();
Any thought or comment?
Thanks in advance!
I had to make a similar decision and opted for this (trimmed for clarity)
class UserModel extends MY_Model
{
public $UserID = 0;
public $CustomerID = null;
public $FirstName = '';
public $LastName = '';
public $EmailAddress = '';
public $Password = null;
public $UserGroupID = true;
function __construct()
{
parent::__construct();
}
private function get($id)
{
$row = $this->get($id);
if ($row !== null)
{
$this->dbResultToObject($row, $this);
}
}
// Return an array of User objects
public function get_list($deleted = false, $userIDToInclude = null)
{
$params = array(null, $deleted, $userIDToInclude);
$query = $this->db->call("spUserGet", $params);
$users = array();
foreach ($query->result() as $row)
{
$user = new UserModel();
$this->dbResultToObject($row, $user);
$users[] = $user;
}
return $users;
}
// Other Methods (Validate, Save etc)
}
I use a mixture of public, protected and private properties so that the reflection code I've written to map the properties from the DB results and to the DB sproc calls then only includes the public properties and prevents too many parameters being sent. But that's getting off-topic so my controller then just looks like:
class Users extends MY_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('UserModel', 'user');
}
....
}
Then a list can be retrieved with
$users = $this->user->get_list();
And a single record with
$user = $this->user->get($userID);
i'm a big fan of thinking about the design in terms of "roles" - not resources. so a "User" is going to be able to get their own Profile. but its only going to be an "Admin" who is able to get All User Profiles.
so that distinction and important separation between what a User can do - get one record - and what an Admin can do - get all records - starts by having separate controllers for each. The User Controller methods are based upon verifying a single user and granting them access to one record. The Admin Controller methods are based upon verifying an admin and granting them access to all records. And this makes sense even from a URL design standpoint - you want your admin area clearly separate.
This separation at the controller makes everything simpler and easier. When you are resource oriented you are constantly checking the credentials in every method and even in your views. Views should be as simple as possible and not be tasked with "is this person an admin"? When you are role oriented - you check the credentials in the controller - and then your methods, the models, and the views are appropriate for what that 'role' needs to accomplish.
Since you are think about OO programming, i think you need to think: what does this class represent?
each instance means one user?
each instance means one user-data-generator?
if it's the first case, it makes sense this class has attributes like $id, $name, $age ....
and the following codes make sense
$user = new User();
$ben = $user->get_user_from_db_with_id($id);
if it's the second case, it shouldn't have the attributes like $id, $name, $age in you sample.
and these codes
$all_user = $ben->get_all_users();
should be replaced with this
$all_user = $user->get_all_users();

Resources