Laravel Backpack 4.0. Save a model then another one (different) - laravel

In my laravel/backpack 4.0 project I have three model:
Product: id, name, ...
Pricebook: id, name, ...
Price: id, pricebook_id, value, ...
I need to create a row in prices table every time a new Product is stored id database.
Is there a method like 'before' or 'after' save to hook?
if not, any other solution?

...I got confused about something trivial, probably a mispelling. This is the solution thank's Martin
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\ProductRequest;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
use App\Models\Price;
class ProductCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation {store as traitStore ;}
use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
public function store()
{
$request = $this->crud->validateRequest();
$response = $this->traitStore();
//get last inserted row id
$entryID = $this->data['entry']->id;
$price = new Price();
$price->product_id = $entryID;
$price->price = 0;
$price->save();
// show a success message
\Alert::success('New price created in DEFAULT_BASE_PRICEBOOK'))->flash();
return $response;
}
...

There are no such hooks, but you can override these methods that control the database insert/update. From the documentation:
Callbacks
Developers coming from GroceryCRUD on CodeIgniter or other CRUD systems will be looking for callbacks to run before_insert, before_update, after_insert, after_update. There are no callbacks in Backpack. The store() and update() code is inside a trait, so you can easily overwrite that method, and call it inside your new method. For example, here's how we can do things before/after an item is saved in the Create operation:
namespace App\Http\Controllers\Admin;
use Backpack\CRUD\app\Http\Controllers\CrudController;
class ProductCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation { store as traitStore; }
// ...
public function store()
{
// do something before validation, before save, before everything
$response = $this->traitStore();
// do something after save
return $response;
}
Source

Related

Getting ID from url and using it in CRUD controller laravel

I need to add orders to existing customers. From my customers.show view I use the following to visit the order form.
href="{{action('OrderController#create', $customer['id'])}}
The link of the page looks like:
/orders/create?2f10f8a0-30b9-11e8-8711-dd477b141226
I've been using the following route::get to no avail to be able to use this ID in my resource controller for Orders
Route::get('orders/create/{id}', 'OrderController#create');
Basically need to be able to to something along the lines of below to be able to show customer details while displaying the create order form as well but getting stuck on best way to pass the id through for the current customer
public function create(){
$customer = Customer::find($id)
return view('orders.create', compact('customer'));
}
With Laravel, you just need to add the parameters you specified in the route as function parameters, like below:
public function create($id){
$customer = Customer::find($id)
return view('orders.create', compact('customer'));
}
You are not passing the $id argument in the create function.
Change it to:
public function create( $id ){
$customer = Customer::find($id)
return view('orders.create', compact('customer'));
}

How to use Laravel Eloquent with complex queries

I am trying to setup Eloquent for a new API that we're working on. I am using relations in a model.
Some relations are complex and aren't really suitable for a quick chained Query Builder statement. For example, I am trying to return metrics and some of those metrics are complex. Such as counting the total clicks generated by a user (it's not just a simple COUNT(*)). Here is the code that I have now:
<?php
namespace App\Models\Eloquent;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
class Affiliate extends Model
{
protected $table = 'user';
public function profile()
{
return $this->belongsTo('App\Models\Eloquent\Profile', 'id');
}
public static function clicks()
{
$sql = "SELECT
user_id,
COUNT(*) / SUM(dummy_metric) AS total_clicks
FROM clicks
WHERE something = 'true'
AND another_thing > 100 # dummy filter for example only
GROUP BY user_id";
$rows = DB::select(DB::raw($sql));
// (PSUEDO CODE) THIS IS WHAT I'D LIKE TO DO IDEALY
return $this->belongsTo($rows, user_id);
}
Possible? I'd like to be able to write our own queries without relying on Query Builder all of the time, but I still want to be able to join the relation to Eloquent.
Assuming you have Clicks model defined, find below the eloquent version of your query.
Click::select(DB:raw("user_id, COUNT(*) / SUM(dummy_metric) AS total_clicks"))
->where(/*condition*/)->groupBy("user_id")->get();
Note:
1) Where method accepts an array of conditions, so you can add more than one condition in same method. Reference
Update
I think thius should be your clicks() method:
public function clicks(){
return $this->hasMany('App\Models\Eloquent\Click',/*relevant ids*/);
}
And, now where you want a count of clicks(in controller for example), you can use following query:
$user = User::find("user_id")->with('clicks')->get();
$clicks = $user->clicks()->count();
To make it more efficient, refer to this article on Tweaking Eloquent relations
Update 2:
You can use Accessor function to retrieve total count
Add following 2 methods in User model.(change clicksCount string to anything you need )
public function clicksCount(){
return $this->hasOne('App\Models\Eloquent\Click')
->select(DB:raw("user_id, COUNT(*) count"))
->groupBy("user_id");
}
public function getClicksCountAttribute(){
return $this->clicksCount->count();
}
Now, you can use $user->clicksCount; to get the count directly.
If you are using Laravel 5.4, you can use withCount() method to easily retrieve the count. Counting Related Models

How does loadByCode() works in magento

How does loadByCode() works? I have been adding a module to my new project and don't know how to use model uses loadByCode method with a given code.
When you look at magento model class it extends Mage_Core_Model_Abstract which intern extends Varien_Object.
The class Mage_Core_Model_Abstract has function like getId(), load(), save(), delete() etc...
Mysql4 resource files are used to perform database queries on tables.
Suppose we have the file Keyur_Test_Model_Mysql4_Test resource file.
<?php
class Keyur_Test_Model_Mysql4_Test extends Mage_Core_Model_Mysql4_Abstract
{
public function _construct()
{
$this->_init('test/test', 'test_id');
}
public function loadByField($field,$value){
$table = $this->getMainTable();
$where = $this->_getReadAdapter()->quoteInto("$field = ?", $value);
$select = $this->_getReadAdapter()->select()->from($table,array('test_id'))->where($where);
$id = $this->_getReadAdapter()->fetchOne($sql);
return $id;
}
}
And here is the model file Keyur_Test_Model_Test
<?php
class Keyur_Test_Model_Test extends Mage_Core_Model_Abstract
{
public function _construct()
{
parent::_construct();
$this->_init('test/test');
}
public function loadByField($field,$value){
$id = $this->getResource()->loadByField($field,$value);
$this->load($id);
}
}
In this file there are many new functions which have been used. Let’s take them one by one. In our model file, we have used this function -
$id = $this->getResource()->loadByField($field,$value);
$this->getResource() function returns the resource model’s object. So we are simply calling the loadyByField($field,$value) function in the resource model.
getTable() function
Now in our Mysql4 file, in the loadByField() function we have used $table = $this->getMainTable(); .This function returns the table name of the current model, i.e the one defined in the class’s constructor. If you want to get table name of another sql table in magento, we need to the getTable() call e.g
$table = $this->getTable(‘newsletter/subscribe’);
_getReadAdapter() function
Next we have:
$where = $this->_getReadAdapter()->quoteInto(“$field = ?”, $value);
Here the first function too see is the $this->_getReadAdapter() function. This function return an object which has function to perform database query, like fetch(), fetchAll(), query(), etc.
_quoteInfo() function
Another function we have used is quoteInto(): This function is used to create our where conditions.
In this the actual value is substituted into the question mark we have written. For e.g $this->_getReadAdapter()->quoteInto(“user_id = ?”, 3); translates to “user_id = 3″.
Another frequently used variation of quoteInfo() is
$where = $this->_getReadAdapter()->quoteInto("$field IN(?)", array(1,2,3));
this translates to “field in (1,2,3)”
Getting deeper into the quoteInto() function: If we have multiple where conditions we can use
$where = $this->_getReadAdapter()->quoteInto("$field = ? AND ", $value).$this->_getReadAdapter()->quoteInto("$field1 = ? OR ", $value1).$this->_getReadAdapter()->quoteInto("$field2 = ?", $value2);
Continuing on the loadByField function in our resource model, next we have
$select = $this->_getReadAdapter()->select()->from($table,array(‘test_id’))->where($where);
Here we have created the select query using the select(), and on that called the from() function.
from() function
The from() function takes many different parameters, if you want to fire a simple select * query, you need to only pass the table name like from($table).
But right now, I wanted to select just a single column that is test_id, so I passed an array with column name to
select. i.e array(‘test_id’).
If we want to select multiple column, we need to array(‘test_id’,’col1’,’col2’).
This will create a sql like “select test_id,col1,col2 from $table”.
But support we want a sql query like “select test_id as id,col1 as column1 from $table” , i would call -
from($table,array('id'=>'test_id','column1'=>'col1'));

Laravel: Returning the namespaced owner of a polymorphic relation

I can find a number of discussions regarding this but no clear solution. Here are two links, although I will cover everything in my own question here.
Github Issues
Laravel.io discussion
Simple Explanation
This is a simple explanation of my problem for anyone already familiar with Laravel's polymorphic relationships.
When using $morphClass, the contents of $morphClass which is saved in the database as the morph "type" is used for the classname when trying to find the owner of a polymorphic relation. This results in an error since the whole point of $morphClass is that it is not a fully namespaced name of the class.
How do you define the classname that the polymorphic relationship should use?
More Detailed Explanation
This is a more detailed explanation explaining exactly what i'm trying to do and why i'm trying to do it with examples.
When using Polymorphic relationships in Laravel whatever is saved as the "morph_type" type in the database is assumed to be the class.
So in this example:
class Photo extends Eloquent {
public function imageable()
{
return $this->morphTo();
}
}
class Staff extends Eloquent {
public function photos()
{
return $this->morphOne('Photo', 'imageable');
}
}
class Order extends Eloquent {
public function photos()
{
return $this->morphOne('Photo', 'imageable');
}
}
The database would look like this:
staff
- id - integer
- name - string
orders
- id - integer
- price - integer
photos
- id - integer
- path - string
- imageable_id - integer
- imageable_type - string
Now the first row of photos might look like this:
id,path,imageable_id,imageable_type
1,image.png,1,Staff
Now I can either access the Photo from a Staff model or a Staff member from a Photo model.
//Find a staff member and dump their photos
$staff = Staff::find(1);
var_dump($staff->photos);
//Find a photo and dump the related staff member
$photo = Photo::find(1);
var_dump($photo->imageable);
So far so good. However when I namespace them I run into a problem.
namespace App/Store;
class Order {}
namespace App/Users;
class Staff {}
namespace App/Photos;
class Photo {}
Now what's saved in my database is this:
id,path,imageable_id,imageable_type
1,image.png,1,App/Users/Staff
But I don't want that. That's a terrible idea to have full namespaced class names saved in the database like that!
Fortunately Laravel has an option to set a $morphClass variable. Like so:
class Staff extends Eloquent {
protected $morphClass = 'staff';
public function photos()
{
return $this->morphOne('Photo', 'imageable');
}
}
Now the row in my database will look like this, which is awesome!
id,path,imageable_id,imageable_type
1,image.png,1,staff
And getting the photos of a staff member works absolutely fine.
//Find a staff member and dump their photos
$staff = Staff::find(1);
//This works!
var_dump($staff->photos);
However the polymorphic magic of finding the owner of a photo doesn't work:
//Find a photo and dump the related staff member
$photo = Photo::find(1);
//This doesn't work!
var_dump($photo->imageable);
//Error: Class 'staff' not found
Presumably there must be a way to inform the polymorphic relationship of what classname to use when using $morphClass but I cannot find any reference to how this should work in the docs, in the source code or via Google.
Any help?
There are 2 easy ways - one below, other one in #lukasgeiter's answer as proposed by Taylor Otwell, which I definitely suggest checking as well:
// app/config/app.php or anywhere you like
'aliases' => [
...
'MorphOrder' => 'Some\Namespace\Order',
'MorphStaff' => 'Maybe\Another\Namespace\Staff',
...
]
// Staff model
protected $morphClass = 'MorphStaff';
// Order model
protected $morphClass = 'MorphOrder';
done:
$photo = Photo::find(5);
$photo->imageable_type; // MorphOrder
$photo->imageable; // Some\Namespace\Order
$anotherPhoto = Photo::find(10);
$anotherPhoto->imageable_type; // MorphStaff
$anotherPhoto->imageable; // Maybe\Another\Namespace\Staff
I wouldn't use real class names (Order and Staff) to avoid possible duplicates. There's very little chance that something would be called MorphXxxx so it's pretty secure.
This is better than storing namespaces (I don't mind the looks in the db, however it would be inconvenient in case you change something - say instead of App\Models\User use Cartalyst\Sentinel\User etc) because all you need is to swap the implementation through aliases config.
However there is also downside - you won't know, what the model is, by just checking the db - in case it matters to you.
When using Laravel 5.2 (or newer) you can use the new feature morphMap to address this issue. Just add this to the boot function in app/Providers/AppServiceProvider:
Relation::morphMap([
'post' => \App\Models\Post::class,
'video' => \App\Models\Video::class,
]);
More about that: https://nicolaswidart.com/blog/laravel-52-morph-map
I like #JarekTkaczyks solution and I would suggest you use that one. But, for the sake of completeness, there's is another way Taylor briefly mentions on github
You can add a attribute accessor for the imageable_type attribute and then use a "classmap" array to look up the right class.
class Photo extends Eloquent {
protected $types = [
'order' => 'App\Store\Order',
'staff' => 'App\Users\Staff'
];
public function imageable()
{
return $this->morphTo();
}
public function getImageableTypeAttribute($type) {
// transform to lower case
$type = strtolower($type);
// to make sure this returns value from the array
return array_get($this->types, $type, $type);
// for Laravel5.7 or later
return \Arr::get($this->types, $type, $type);
// which is always safe, because new 'class'
// will work just the same as new 'Class'
}
}
Note that you still will need the morphClass attribute for querying from the other side of the relation though.
This is the way you can get morph class name(alias) from Eloquent model:
(new Post())->getMorphClass()
Let laravel put it into the db - namespace and all. If you need the short classname for something besides making your database prettier then define an accessor for something like :
<?php namespace App\Users;
class Staff extends Eloquent {
// you may or may not want this attribute added to all model instances
// protected $appends = ['morph_object'];
public function photos()
{
return $this->morphOne('App\Photos\Photo', 'imageable');
}
public function getMorphObjectAttribute()
{
return (new \ReflectionClass($this))->getShortName();
}
}
The reasoning I always come back to in scenarios like this is that Laravel is pretty well tested, and works as expected for the most part. Why fight the framework if you don't have to - particularly if you are simply annoyed by the namespace being in the db. I agree it isn't a great idea to do so, but I also feel that I can spend my time more usefully getting over it and working on domain code.
In order for eager loading of polymorphic relations eg Photo::with('imageable')->get(); it's necessary to return null if type is empty.
class Photo extends Eloquent {
protected $types = [
'order' => 'App\Store\Order',
'staff' => 'App\Users\Staff'
];
public function imageable()
{
return $this->morphTo();
}
public function getImageableTypeAttribute($type) {
// Illuminate/Database/Eloquent/Model::morphTo checks for null in order
// to handle eager-loading relationships
if(!$type) {
return null;
}
// transform to lower case
$type = strtolower($type);
// to make sure this returns value from the array
return array_get($this->types, $type, $type);
// which is always safe, because new 'class'
// will work just the same as new 'Class'
}
}
A simple solution will be to alias the value in imageable_type column in the db to the full class namespace as:
// app/config/app.php
'aliases' => [
...
'Order' => 'Some\Namespace\Order',
'Staff' => 'Maybe\Another\Namespace\Staff',
...
]

How to update field when delete a row in laravel

Let I have a table named customer where customer table has a field named deleted_by.
I implement softDelete in customer model. Now I want to update deleted_by when row delete. So that I can trace who delete this row.
I do search on google about it But I don't found anything.
I use laravel 4.2.8 & Eloquent
You may update the field using something like this:
$customer = Customer::find(1); // Assume 1 is the customer id
if($customer->delete()) { // If softdeleted
DB::table('customer')->where('id', $customer->id)
->update(array('deleted_by' => 'SomeNameOrUserID'));
}
Also, you may do it in one query:
// Assumed you have passed the id to the method in $id
$ts = Carbon\Carbon::now()->toDateTimeString();
$data = array('deleted_at' => $ts, 'deleted_by' => Auth::user()->id);
DB::table('customer')->where('id', $id)->update($data);
Both is done within one query, softDelete and recorded deleted_by as well.
Something like this is the way to go:
// override soft deleting trait method on the model, base model
// or new trait - whatever suits you
protected function runSoftDelete()
{
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
$this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
$deleted_by = (Auth::id()) ?: null;
$query->update(array(
$this->getDeletedAtColumn() => $this->fromDateTime($time),
'deleted_by' => $deleted_by
));
}
Then all you need is:
$someModel->delete();
and it's done.
I would rather use a Model Event for this.
<?php
class Customer extends \Eloquent {
...
public static function boot() {
parent::boot();
// We set the deleted_by attribute before deleted event so we doesn't get an error if Customer was deleted by force (without soft delete).
static::deleting(function($model){
$model->deleted_by = Auth::user()->id;
$model->save();
});
}
...
}
Then you just delete it like you would normally do.
Customer::find(1)->delete();
I know this is an old question, but what you could do (in the customer model) is the following....
public function delete()
{
$this->deleted_by = auth()->user()->getKey();
$this->save();
return parent::delete();
}
That would still allow the soft delete while setting another value just before it deletes.

Resources