Laravel Backpack : Storing Belongs To Many relationships using custom view - laravel

I have a flight class and this flight has a custom view field like so:
This represents a belongs to many relationship which stores website_id / flight_id and pricing as pivot data in a pivot table.
The custom view uses JS to send this data back to the controller in this format:
{"1":{"price_adult":"434","price_child":"545"},"2":{"price_adult":"323","price_child":"324"},"3":{"price_adult":"434","price_child":"43"}}
Trying to send this data with the request doesn't create the relations fields, and because I do not have a flight ID at the point of creating this within the controller I can not loop this JSON to make the relations manually.
Can anyone point out what the best course of action is or if there is support for this? I took a look at the docs but they are woefully short and patchy in terms of being much help.
EDIT:
I should have said I can probably make this work using a custom name attribute on the model for the relation, then add a set mutator to loop this data and update the prices relation but I don't want to go down this route if there is support for this I am missing out of the box in backpack.
EDIT2:
Someone asked about the relation:
$this->belongsToMany(Website::class, 'website_pricing')->withPivot('price_adult', 'price_child');
This is working fine its not a problem with the relation working its how can I get backpack to store the data as a relation when the flight has no ID yet, or how can I pass the data I posted above in such a way that the backpack crud controller can handle it?

You may need to create a flight first, if no flight id is being provided. Can you explain the database relational structure more?

Basically thought I should post what I did because no one could provide an answer to this.
So basically you have to copy the store / update functions from the parent, changing a few lines.
$this->crud->hasAccessOrFail('create');
// fallback to global request instance
if (is_null($request)) {
$request = \Request::instance();
}
// replace empty values with NULL, so that it will work with MySQL strict mode on
foreach ($request->input() as $key => $value) {
if (empty($value) && $value !== '0') {
$request->request->set($key, null);
}
}
// insert item in the db
$item = $this->crud->create($request->except(['save_action', '_token', '_method']));
$this->data['entry'] = $this->crud->entry = $item;
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
// save the redirect choice for next time
parent::setSaveAction();
return parent::performSaveAction($item->getKey());
Basically any line which references a function in the parent class using $this->method needs to be changed to parent::
This line is what I used to submit the relations JSON string passed to the controller as relations $item->prices()->sync(json_decode($request->input('prices'), true));
This is done after the line containing $item = $this->crud->create as the item id that just got stored will be available at that point.

Related

How can I get the data inside my notifications from within my Controller?

I'm trying to get the numbers of records from my notifications, where the candidate_user_id column from inside the data attribute is the same as the UserId (Authenticated User).
After I dd, I was able to get the data from all of the records in the table by using the pluck method (Line 1). I then tried to use the Where clause to get the items that I need
but this just didn't work, it was still returning all of the records in the table.
DashboardController.php:
public function index()
{
$notifications = Notification::all()->pluck('data');
$notifications->where('candidate_user_id', Auth::user()->id);
dd($notifications);
}
Here is a partial screenshot of the data that is being plucked.
How can I get the data from this, in a way like this ->where('candidate_user_id', Auth::user()->id);?
If data was a JSON field on the table you could try to use a where condition to search the JSON using the -> operator:
Notification::where('data->candidate_user_id', Auth::id())->pluck('data');
Assuming you only want this data field and not the rest of the fields, you can call pluck on the builder directly. There isn't much reason to hydrate Model instances with all the fields to then just pluck a single field from them if it is just a table field, so you can ask the database for just the field you want.
The data in the data field is a json string, so you can tell Laravel to automatically cast it as an array using the $casts property on each of the models that is notifiable.
For instance, if you have a User model which uses the trait (ie has use Notifiable), add this:
protected $casts = [
'data' => 'array',
];
If you want to access all notifications for the auth user.
$user = auth()->user();
dd($user->notifications->pluck('data'));
If you really want to do in your question way, here is how.
$notifications = Notification::all()->pluck('data');
$notifications = $notifications->where('candidate_user_id', Auth::user()->id)
->all();
This assumes you that you did not modify the default laravel notifications relationship and database migration setup. If you have modified some of the default ones, you need to provide how you modify it.

Laravel nova overriding/setting custom value for the Fields or Has Through relationship

I am developing a Web application using Laravel. For the admin panel, I am using Laravel Nova. What I am trying to do now is that I need to use data from the table which has relationship through another table. To be, clear, see my database structure below.
items
=====
id
name
price
sub_category_id
sub_categories
==============
id
name
parent_category_id
parent_categories
=================
id
name
What I am trying to achieve inside the Nova is that I want to display the parent category name of the item on the item index/list page. The first thing is that I do not want to create custom attribute something like this in the model
protected $appends = [
'parent_category_name'
];
function getParentCategoryNameAttribute()
{
//code here
}
Therefore, there are two solutions I can think of. The first solution is using the HasThrough relationship. But I cannot find it in Nova. So, I cannot use it. The second solution is that overriding the field value on render. Something like this.
Text::make("fieldname")->fillUsing(function($request, $model, $attribute, $requestAttribute) {
//$model->sub_category->parent_category - then I can return a value
return "Parent category name";
})->onlyOnIndex()
But the above code is not working. So, what would be the best approach to handle the has-through relationship in Nova?
Assuming you have defined the relationship sub_category & parent_category properly.
Define the relationship in Item model as below
public function parent_category()
{
return $this->sub_category->parent_category();
}
Then use it in Item resource as below.
BelongsTo::make('Parent Category')
->hideWhenCreating()
->hideWhenUpdating(),

Laravel 5.3 - caching information schema queries

I use multiple domains for the same site structure, so almost all tables have the domain_id column. So, to NOT define the domain_id condition every time I am using the following approach (not sure if this is the best one)
I created BaseModel that other models are extending from and have this in boot method of that BaseModel
parent::boot();
static::addGlobalScope(new DomainScope());
and here is the apply method's content according to docs
if (Schema::hasColumn($model->getTable(), 'domain_id')) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
This works great, however if I have e.g. 5 find queries on the same page, to the same table (just as an example) in debug panel I see 5 queries like this
select column_name from information_schema.columns where table_schema = 'my_db_name' and table_name = 'my_table_name'
Now, I perfectly understand that to check whether the column exists in the table it gets the information from information schema, but why it is making the same query for the same table over and over again. I would presume It should make one request and then cache it, and for subsequent requests just read from cache.
q1) Does laravel internally cache this query ? I am thinking maybe because debug is enabled, that is why each time its making a query ? but cant find any verification of this
q2) and if it does not cache, can I cache it manually. I checked the laravel docs for adding a cache, but the problem here that query is not done by me, so I cant figure out to simply use Cache::remember
Thanks
Well, I would suggest not to fire a query for checking if domain_id exists in the table every time. You can do this by simply adding a variable to your model that defines whether the model is a multi-domain model (has a domain_id column) or single domain model (no domain_id column) like so:
// In BaseModel.php
public $multiDomain = true; // override to false in your child model class if it's a single domain model
// In DomainScope apply method
if ($model->multiDomain)) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
If you don't want to use the above, you can cache like so:
if(Cache::remember($model->getTable() . "_domain", $minutes, function() use($model) {
return Schema::hasColumn($model->getTable(), 'domain_id');
}) {
$builder->where('domain_id', '=', DOMAIN_ID);
}

Querying multiple table to get specific data with Eloquent ORM

I am quite new to Laravel 4 and its great Eloquent ORM. I have four tables such as :
Sector (iSectorCode);
MailingSector (iSectorCode, iMailingCode);
Mailing (iMailingCode);
MailingLanguages(iMailingCode, sTranslation);
I have the sector id, and I want to get all Mailings associated. But I also need to reach the MailingLanguages table containing the content translations for a specific Mailing.
So for now I can get all Mailings for a specific sector doing :
Sector::find($iSectorCode)->mailings()->get()->toArray();
But doing Sector::find($iFormSectorCode)->mailings()->mailingsLanguages()->get()->toArray(); don't work even if the relation between Mailing and MailingLanguages is defined :
public function mailingsLanguages(){
return $this->hasMany('MailingLanguage','iMailingCode');
}
So I don't know how to get all translations for a specific Mailing, for a specific Sector.
Providing that you've setup relationships between all of the tables, you can request that they be grabbed with the initial request.
$sector = Sector::with('mailings', 'mailings.languages')->find($iSectorCode);
This will create a nice join that will include related records for Mailing, then their related records for MailingLanguage, as well as the requested Sector.
The above example does assume that Sector has a relationship called mailings and that Mailing has a relationship called languages.
You could also load them in after the fact.
$sector = Sector::find($iSectorCode);
$sector->load(['mailings', 'mailings.languages']);
I would recommend making use of the findOrFail method that laravel provides.
try {
$sector = Sector::with(['mailings', 'mailings.langauges'])
->findOrFail($iSectorCode);
} catch(ModelNotFoundException $e) {
// do something here
}
This saves having to check whether $sector returned anything, as an exception will be thrown.
Hope that helps.

How do I keep models loosely coupled when querying a many-to-many table in CodeIgniter?

I'm using CodeIgniter and have three tables and a model for each:
User - table of users
Product - table of products
UserProduct - table showing which users have which products (two foreign key columns, and some other columns such as date)
In my code I want to display a list for a particular user showing which products they have. So I have a method in the UserProduct class that does a select on the UserProduct table matching for the userId, and a join bringing in all the data I need about each product.
This works fine, but I am concerned now that I am moving down a path of tight coupling. For example, say I want to find out the URL of a product image. I have a method in the product model to return this, I don't want a duplicate method in the UserProduct model, and I don't want to create a coupling by having the UserProduct model trying to access methods in other models (which I don't think CodeIgniter likes you to do).
This problem seems likely to occur in any model that accesses a DB table that has foreign keys where that model may need data from the parent table and want to manipulate it.
Is there an elegant solution, or do I need to accept that foreign keys necessarily create couplings in models or the need for similar methods across models?
TIA,
Phil.
My personal standard is to create 1 controller + 1 model always.
for example site.com/users will use model_users.php
or
site.com/products will use model_products.php
to slim your code and re-use methods use methods params for example a model method to be re-used could be:
function getUsers($data){
foreach($data as $key=>$value){
$this->db->where($key,$value);
}
$query = $this->db->get('users');
return $query->result();
}
the more you extend this method the more you can re-use, just another example of extending:
function getUsers($data,$order_by = false){
foreach($data as $key=>$value){
$this->db->where($key,$value);
}
if($order_by){
foreach($order_by as $key=>$value){
$this->db->order_by($key,$value);
}
}
$query = $this->db->get('users');
return $query->result();
}
//then you call results with this in your controller
$results = $this->model->getUsers(
$data = array('id'=>'12','country'=>'USA'),
$order_by = array('username'=>'DESC')
);
so now you got also order_by , and so on, you can add joins, limit,offset,like etc.. all in the same or quite the same way , the more accurated you are the more you can re-use the same methods

Resources