Laravel. setRelation(s) - adds only the last relationship - php

php: 7.3.4
Laravel Framework 5.7.28
Hi, everybody. Help add data for all relations.
Now for some reason it turns out that only the last relation is added.
setRelations works similarly.
code
public static function firstOrCreateModel() {
$cart = Cart::firstOrCreate(self::getWhereQuery());
if ($cart->delivery === 1) {
$cart->setRelation('np_area', $cart->with('npArea')->first());
$cart->setRelation('np_city', $cart->with('npCity')->first());
$cart->setRelation('np_warehouse', $cart->with('npWarehouse')->first());
dump($cart->toArray());
}
return $cart;
}
Result. GIF screen recording

setRelation() does not save to the DB setRelation its just for testing you better use sync() if its many-to-many or attach() if its one-to-many

Related

Laravel Nova HasOne Relationship view and edit 2 models in single panel

I have a HasOne relationship between 2 models.
In Laravel Nova, is it possible to merge them into a single panel when viewing details, and update both model together when updating details.
This feature is not officially supported in laravel nova. but there is a workaround to achieve it using model accessors and mutators. this answer can help you
Example:
// in the model
public function getProfilePictureAttribute()
{
// Don't forget to check the relation here if you don't want any error on Nova.
return $this->profile ? $this->profile->picture : null;
}
public function setProfilePictureAttribute($value)
{
$profile = $this->profile;
$profile->fill(['picture' => $value]);
$profile->save();
}
// in the resource
Image::make('Profile picture')
This question is quite old... but for Nova 4, this could be easily done by adding ->required()...
HasOne::make('Address')->required(),
MorphOne::make('Address')->required(),
etc... This will display the fields of second resource on the same page, and insert/update accordingly.

Creating Laravel authorisation policy using something either than 'user->id'

I would like to create a Laravel Authorisation Policy, however rather than checking the user->id I would like to check the related users Business model (like $user->business()->id)
I've tried using the following in my OrderPolicy but it does not work.
OrderPolicy
class OrderPolicy
{
....
public function edit(User $user, Order $order)
{
if ($user->business()->id === $order->business_id) {
return true;
}
}
}
Blade
...
#can('edit', $business->orders())
Edit Link
#endcan
...
Could someone show me how I could do this correctly?
Assuming business() is a relationship method.
$user->business->id would be the id of the Business model that is related to the user.
May want to check that ->business isn't null first.
You can also query directly on the relationship if you don't want to load that relationship. $user->business()->where('id', $order->business_id)->exists()
Laravel 5.4 Docs - Eloquent - Relationships - Relationship Methods vs Dynamic Properties

How to retrieve grandchildren through a polymorphic relation in Laravel 5.2?

My database design looks like the below diagram.
A course has many modules
Modules morphs to Scorm and other 2 tables (not used in this example)
A scorm Morph one Module and has Many Scoes
I am trying to access from my course model all the scoes related using:
$course->scoes.
I am using Laravel 5.2, using the Eloquent relations I know this is not possible. After few tests, using the query builder I actually can return the correct data, but, they come back as being an instance of Module::class instead of Scorm, and Sco.
This is the code I have now.
Thanks,
public function modules() {
return $this->hasMany(Module::class);
}
public function scorms(){
return $this->modules()->where('moduleable_type','=','scorms');
}
public function scoes(){
return $this->scorms()->select('scoes.*')
->join('scoes','modules.moduleable_id','=','scoes.scorm_id');
}
I'm not sure but this may be exactly what you want.
https://laravel.com/docs/5.2/eloquent-relationships#has-many-through
I found a way to do what I was trying to do. This way makes only 2 queries to the db and it's located in the Sco Model.
//returns a Course Object.
$sco->course()
/**
* Get the course
* #return App\Course|null
*/
public function Course(){
$o = $this
->join('scorms','scoes.scorm_id','=','scorms.id')
->join('modules',function($join){
$join
->on('moduleable_id','=','scorms.id')
->where('moduleable_type','=','scorms');
})
->join('courses','modules.course_id','=','courses.id')
->select('courses.id as course_id')
->first();
if(!$o) return null;
return Course::find($o->course_id);
}

Check if belongsToMany relation exists - Laravel

Two of my tables (clients and products) have a ManyToMany relation using Laravel's blongToMany and a pivot table.
Now I want to check if a certain client has a certain product.
I could create a model to check in the pivot table but since Laravel does not require this model for the belongsToMany method I was wondering if there is another way to check if a certain relationship exists without having a model for the pivot table.
I think the official way to do this is to do:
$client = Client::find(1);
$exists = $client->products->contains($product_id);
It's somewhat wasteful in that it'll do the SELECT query, get all results into a Collection and then finally do a foreach over the Collection to find a model with the ID you pass in. However, it doesn't require modelling the pivot table.
If you don't like the wastefulness of that, you could do it yourself in SQL/Query Builder, which also wouldn't require modelling the table (nor would it require getting the Client model if you don't already have it for other purposes:
$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;
The question is quite old but this may help others looking for a solution:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
No "wastefulness" as in #alexrussell's solution and the query is more efficient, too.
Alex's solution is working one, but it will load a Client model and all related Product models from DB into memory and only after that, it will check if the relationship exists.
A better Eloquent way to do that is to use whereHas() method.
1. You don't need to load client model, you can just use his ID.
2. You also don't need to load all products related to that client into memory, like Alex does.
3. One SQL query to DB.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
Update: I did not take into account the usefulness of checking multiple relations, if that is the case then #deczo has a way better answer to this question. Running only one query to check for all relations is the desired solution.
/**
* Determine if a Client has a specific Product
* #param $clientId
* #param $productId
* #return bool
*/
public function clientHasProduct($clientId, $productId)
{
return ! is_null(
DB::table('client_product')
->where('client_id', $clientId)
->where('product_id', $productId)
->first()
);
}
You could put this in you User/Client model or you could have it in a ClientRepository and use that wherever you need it.
if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}
But if you already have defined the belongsToMany relationship on a Client Eloquent model, you could do this, inside your Client model, instead:
return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);
#nielsiano's methods will work, but they will query DB for every user/product pair, which is a waste in my opinion.
If you don't want to load all the related models' data, then this is what I would do for a single user:
// User model
protected $productIds = null;
public function getProductsIdsAttribute()
{
if (is_null($this->productsIds) $this->loadProductsIds();
return $this->productsIds;
}
public function loadProductsIds()
{
$this->productsIds = DB::table($this->products()->getTable())
->where($this->products()->getForeignKey(), $this->getKey())
->lists($this->products()->getOtherKey());
return $this;
}
public function hasProduct($id)
{
return in_array($id, $this->productsIds);
}
Then you can simply do this:
$user = User::first();
$user->hasProduct($someId); // true / false
// or
Auth::user()->hasProduct($someId);
Only 1 query is executed, then you work with the array.
The easiest way would be using contains like #alexrussell suggested.
I think this is a matter of preference, so unless your app is quite big and requires a lot of optimization, you can choose what you find easier to work with.
Hello all) My solution for this problem: i created a own class, extended from Eloquent, and extend all my models from it. In this class i written this simple function:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
For make a check existing relation you must write something like:
if ($user->have('subscribes', 15)) {
// do some things
}
This way generates only a SELECT count(...) query without receiving real data from tables.
To check the existence of a relationship between 2 models, all we need is a single query against the pivot table without any joins.
You can achieve it using the built-in newPivotStatementForId method:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
use trait:
trait hasPivotTrait
{
public function hasPivot($relation, $model)
{
return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
}
}
.
if ($user->hasPivot('tags', $tag)){
// do some things...
}
This has time but maybe I can help someone
if($client->products()->find($product->id)){
exists!!
}
It should be noted that you must have the product and customer model, I hope it helps,

Propel: how to remove link made via many-to-many relation

(link to previous question just in case: Struggling with one-to-many relation in an admin form)
I have this many-to-many relation in my Symfony-1.3 / Propel-1.4 project between User and Partner. When the User is being saved, if it has certain boolean flag being true, I want to clear all the links to the partners. Here is what I do at the moment and it doesn't work:
// inside the User model class
public function save(PropelPDO $con = null) {
if ($this->getIsBlaBla()) {
$this->setStringProperty(NULL);
$this->clearUserPartners();
}
parent::save($con);
}
Setting the string property to NULL works; looking at the DB clearly shows it. Thing is however, the USER_PARTNER table still holds the relations between the users and the partners. So I figured I have to clear the links one by one, like this:
foreach($this->getUserPartners() as $user_partner) {
$user_partner->delete();
//UserPartnerPeer::doDelete($user_partner); // tried that too
}
Both don't do the trick.
As I mentioned in my previous question, I am just monkey-learning Symfony via trial and error, so I evidently miss something very obvious. Please point me in the right direction!
EDIT: Here is how I made it work:
Moved the code to the Form class, like so:
public function doSave(PropelPDO $con = null) {
parent::doSave($con);
if ($this->getObject()->getIsSiteOwner()) {
$this->getObject()->setType(NULL);
$this->getObject()->save();
foreach($this->getObject()->getUserPartners() as $user_partner) {
$user_partner->delete();
}
}
return $this->getObject();
}
public function updateObject($values = null) {
$obj = parent::updateObject($values);
if ($obj->getIsSiteOwner()) {
$obj->clearUserPartners();
}
return $this->object;
}
What this does is:
When the boolean flag `is_site_owner` is up, it clear the `type` field and **saves** the object (ashamed I have not figured that out for so long).
Removes all existing UserPartner many-to-many link objects.
Clears newly associated (via the DoubleList) UserPartner relations.
Which is what I need. Thanks to all who participated.
Okey so now you have a many-to-many relation where in database terms is implemented into three tables (User , Parter and UserPartner). Same thing happens on Symfony and Propel, so you need to do something like this on the doSave method that should declare in UserForm:
public function doSave($con = null)
{
parent::doSave($con); //First all that's good and nice from propel
if ($this->getValue('please_errase_my_partners_field'))
{
foreach($this->getObject()->getUserPartners() as $user_partner_relation)
{
$user_partner_relation->delete();
}
}
return $this->getObject();
}
Check the method name "getUserPartners" that should be declared on the BaseUser.class.php (lib/model/om/BaseUser.class.php)
If you are learning Symfony, I suggest you use Doctrine instead of Propel because, I think Doctrine is simplier and more "beautiful" than Propel.
For your problem, I think you are on the good way. If I were you, I will keep my function save() I will write an other function in my model User
public function clearUserPartners(){
// You have to convert this query to Propel query (I'm sorry, but I don't know the right syntax)
"DELETE FROM `USER_PARTNER` WHERE user_id = '$this->id'"
}
With this function, you don't must use a PHP foreach.
But I don't understand what is the attribute StringProperty...
UserPartnerQuery::create()->filterByUser( $userObject )->delete();
or
UserPartnerQuery::create()->filterByUser( $partnerObject )->delete();
Had the same problem. This is a working solution.
The thing is that your second solution, ie. looping over the related objects and calling delete() on them should work. It's the documented way of doing things (see : http://www.symfony-project.org/book/1_0/08-Inside-the-Model-Layer#chapter_08_sub_saving_and_deleting_data).
But instead of bombing the DB with delete queries, you could just as well delete them in one go, by adding a method to your Peer class that performs the deletion using a simple DB query.

Categories