How to use Relation::morphMap() for diffrent class - mysql

I am using laravel polymorphic relation.
I have defined two morphTo relations for two purpose.
My question is that ,but when I am defining the key of Relation::morphMap() function array , then my array key is same for one case, so I want to know is there any way by which I can specify that I am defining relation for specific class.
My first relation....
Package.php
public function provider()
{
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function packages()
{
return $this->morphMany(VendorPackage::class, 'map', 'map_type_id', 'map_id');
}
Vendor.php
public function packages()
{
return $this->morphMany(VendorPackage::class, null, 'map_type_id', 'map_id');
}
I want to set the key to compare with map_type_id so I am setting the key in service provider.
Relation::morphMap([
config('evibe.roles.planner') => \Vendor::class,
config('evibe.roles.artist') => \Vendor::class,
config('evibe.roles.venue') => \Venue::class,
], false);
My 2nd morphTo relation
Ticket Booking.php
public function provider()
{
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function bookings()
{
return $this->morphMany(TicketBooking::class,null,'map_type_id','map_id');
}
Decors.php
public function bookings()
{
return $this->morphMany(TicketBooking::class,null,'map_type_id','map_id ');
}
Now again I have to define the morphTo in service provider because I am not using the default Model name.
so my morphTo in service providers became like this.
Relation::morphMap([
config('evibe.roles.planner') => \Vendor::class,
config('evibe.roles.artist') => \Vendor::class,
config('evibe.roles.venue') => \Venue::class,
config('evibe.ticket_type.venues') => \Venue::class,
config('evibe.ticket_type.decors') => \Decor::class
], false);
Now my problem is that key config('evibe.roles.planner') and config('evibe.ticket_type.venues) has the same value 3, so when both things is accessed by the relationship then it is throwing error, because array have same key.
So I want to ask is there any other way to define different morphMap for different relationship.

Lets start by defining the polymorphic relations
First relation....
Package.php
public function provider() {
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function packages() {
// you should provide the relation name, in our exemple its called `provider` as a second parameter
return $this->morphMany(VendorPackage::class, 'provider', 'venues');
}
Vendor.php
public function packages() {
// you should provide the relation name, in our exemple its called `provider` as a second parameter
return $this->morphMany(VendorPackage::class, 'provider', 'vendors');
}
Second Relation
TicketBooking.php
public function provider() {
return $this->morphTo(null, 'map_type_id', 'map_id');
}
Venue.php
public function bookings() {
return $this->morphMany(TicketBooking::class, 'provider', 'venues');
}
Decors.php
public function bookings() {
return $this->morphMany(TicketBooking::class, 'provider', 'decors');
}
and register Relation::morphMap as
Relation::morphMap([
'vendors' => \Vendor::class,
'venues' => \Venue::class,
'decors' => \Decor::class
]);

Related

Symfony CollectionType and Doctrine : Remove null items

I'm having an issue with Symfony's CollectionType.
My project is an API, receiving form values as JSON input.
I want to be able to update an entity named "Reservation", which has a collection of items (called "ReservationItems", each item is an embedded form of type "ReservationItemType" :
class ReservationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
// ...
->add(
'reservationItems',
CollectionType::class,
[
'entry_type' => ReservationItemType::class,
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'by_reference' => false,
]
)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Reservation::class,
]);
}
}
Here is the code of ReservationItemType :
class ReservationItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'name',
TextType::class,
[
]
)
->add(
'status',
TextType::class,
[
]
)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => ReservationItem::class,
]);
}
}
Here is the Reservation entity definition :
#[ORM\Entity(repositoryClass: ReservationRepository::class)]
class Reservation
{
// ...
/** #var ReservationItem[]|ArrayCollection */
#[ORM\OneToMany(mappedBy: 'reservation', targetEntity: ReservationItem::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
private Collection $reservationItems;
public function __construct()
{
$this->reservationItems = new ArrayCollection();
}
/**
* #return Collection<int, ReservationItem>
*/
public function getReservationItems(): Collection
{
return $this->reservationItems;
}
public function addReservationItem(ReservationItem $reservationItem): self
{
if (!$this->reservationItems->contains($reservationItem)) {
$this->reservationItems->add($reservationItem);
$reservationItem->setReservation($this);
}
return $this;
}
public function removeReservationItem(ReservationItem $reservationItem): self
{
if ($this->reservationItems->removeElement($reservationItem)) {
// set the owning side to null (unless already changed)
if ($reservationItem->getReservation() === $this) {
$reservationItem->setReservation(null);
}
}
return $this;
}
}
And here is the ReservationItem entity definition :
#[ORM\Entity(repositoryClass: ReservationItemRepository::class)]
class ReservationItem
{
// ...
#[ORM\ManyToOne(targetEntity: Reservation::class, inversedBy: 'reservationItems')]
#[ORM\JoinColumn(nullable: false)]
private ?Reservation $reservation = null;
public function getReservation(): ?Reservation
{
return $this->reservation;
}
public function setReservation(?Reservation $reservation): self
{
$this->reservation = $reservation;
return $this;
}
}
From my client app, here is what I am sending :
{
"reservation": {
"reservationItems": [
{ "name": "Foo", "status": "PENDING"},
null,
{ "name": "Bar", "status": "PENDING"}
]
}
]
In Symfony's doc, each example is based on form-data, which can be represented by :
reservation[reservationItems][0][name]: Foo
reservation[reservationItems][0][status]: PENDING
reservation[reservationItems][2][name]: Bar
reservation[reservationItems][2][status]: PENDING
This results in an indexed PHP array, with missing index "1".
Symfony considers former item which was indexed as 1 is missing, and delete this item.
But in JSON, it is not possible to manually index an array, with missing values.
My solution is to send null on indexes which must be deleted.
But Symfony is ignoring null values, and doesn't call Reservation::removeReservationItem method, even with by_reference set to false.
I'm using GraphQL, with webonyx/graphql-php. I tried another solution by sending an object instead of an array, but the library ignores "indexes"...
Did someone has met this issue (with GraphQL or REST or other JSON based protocol) ?
Do you have any advice ?
I have followed Symfony's recommandations by setting delete_empty to true, and even with required set to false or empty_data set to null, nothing works as expected.

hasOne with null-able in laravel not working

I have a customer table which has a field called 'policy_id', where policy_id points to policy table. It is a null-able field, ie. Some customers may not have a policy.
I have a relationship code like this in Customer.php
public function policy() {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
}
But when I issue a search request I am getting error like this:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\Policy]
If I modify the function like this:
public function policy() {
if ($this->getAttribute('policy_id')) {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
} else {
return null
}
}
But I am getting an error like this:
Call to a member function getRelationExistenceQuery() on null
Here is my search code:
$c = new Customer();
return Customer::doesntHave('policy')->orWhere(function (Builder $query) use ($req) {
$query->orWhereHas('policy', function (Builder $query) use ($req) {
$p = new Policy();
$query->where($req->only($p->getFillable()))
->orWhereBetween("policy_period_from", [$req->policy_period_start_from, $req->policy_period_start_to])
->orWhereBetween("policy_period_to", [$req->policy_period_end_from, $req->policy_period_end_to])
->orWhereBetween("payment_date", [$req->payment_date_from, $req->payment_date_to]);
});
})->where($req->only($c->getFillable()))->get();
Am I missing something or are there any other ways to do this?
PS: While debugging the above search code is returning successfully, but the exception happening from somewhere inside Laravel after the prepareResponse call.
Thanks in advance.
return $this->hasOne('App\ModelName', 'foreign_key', 'local_key');
Change the order, put the foreign_key policy_id in front of id
In your Customer Model, you need to use belongsTo method:
public function policy() {
return $this->belongsTo('App\Models\Policy', "policy_id", "id");
}
And In your Policy Model, use hasOne:
public function customer() {
return $this->hasOne('App\Models\Customer', "policy_id", "id");
}
First of all, you placed the wrong params.
$this->belongsTo('App\Models\Policy', "FK", "PK");
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id');
}
And for null value of policy_id you can use withDefault();
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id')->withDefault([
'name' => 'test'
]);;
}
there's a number of problems there but can you perhaps specify the namespace and the class of both your models - Customer and Policy.
By default, the models you create with php artisan make:model will use the \App namespace e.g. \App\Customer and \App\Policy.
Just double check that.
Also, with regards to the relationship, if the Laravel conventions have been followed you could just:
In the Customer model
public function policy() {
return $this->belongsTo(Policy::class);
}
In the Policy model
public function customer() {
return $this->hasOne(Customer::class);
}
of if a multiple customers can be under one policy
public function customers() {
return $this->hasMany(Customer::class);
}
Good luck

Yii2 property mapping to tablename

I use Yii2 2.0.9 basic template and I try to set up my class.
I my class I use references of other classes in my property.
/**
*
*#property Contact contact
*/
class User extends ActiveRecord {
public static function tableName() {
return "user";
}
/**
* This is want I need
*/
public function databaseMapping(){
return [
"contact" => "contact_id"
];
}
}
Is there in Yii2 a solution for my problem?
Thanks Marvin Thör
In Grails I can write this:
class User {
Contact contact
Boolean passwordExpired
static mapping = {
contact(column: 'contact_id')
passwordExpired(column: 'password_expired')
}
}
User user = new User();
user.passwordExpired = true
user.contact = new Contact();
and I want the same
You might want to use the method attributeLabels() inside your model class to define label names to show to the end user.
public function attributeLabels() {
return [
'contact_id' => 'Contact',
];
}
However, there are times like when creating a RESTful API using Yii2 that you need to return a json with fields with specific field names. For these ocasions, you can use the fields() method:
public function fields() {
return [
'contact' => 'contact_id',
];
}
This method returns the list of fields that should be returned by default by toArray(). You can check more about it HERE.
Change your labels and db column remain unchanged.
public function attributeLabels()
{
return [
'contact_id' => Yii::t('app', 'Use your name here'),
];
}

create Notification in yii2

I am new to yii. I have created two table, user and notification. The table notification has userid as a foreign key. I want to create notification against user in user model just like I get notification from user model
public function getnotifications()
{
return $this->hasMany(Notification::className(), ['user_id' => 'id']);
}
Use this function in your model.
public function addNotification() {
$notification = new Notification();
$notification->user_id = $this->id;
$notification->message = "Notification";
$notification->save();
}
Apart from the name of your function ( should be getNotifications() not getnotifications() ), I see nothing wrong in your code.
public function getNotifications()
{
return $this->hasMany(Notification::className(), ['user_id' => 'id']);
}
What is the problem now?

Laravel 5.1.* Custom Pivot Class Array Not Casted

I have a Model called User and another Model called Roles and they are linked with each other through a belongsToMany relationship. But I needed to cast certain pivot attributes so I used a custom pivot class RoleUserPivot which basically looks like follows:
...
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUserPivot extends Pivot
{
protected $casts = [
'active' => 'boolean',
'permissions' => 'array',
];
}
...
The relationship definition in User and Role models is as follows:
...
// User Model
public function roles()
{
return $this
->belongsToMany('App\Role')
->withPivot(
'active',
'permissions'
);
}
public function newPivot(Model $parent, array $attributes, $table, $exists)
{
if ($parent instanceof Role) {
return new RoleUserPivot($parent, $attributes, $table, $exists);
}
return parent::newPivot($parent, $attributes, $table, $exists);
}
...
And similarly:
...
// Role Model
public function users()
{
return $this
->belongsToMany('App\User')
->withPivot(
'active',
'permissions'
);
}
public function newPivot(Model $parent, array $attributes, $table, $exists)
{
if ($parent instanceof User) {
return new RoleUserPivot($parent, $attributes, $table, $exists);
}
return parent::newPivot($parent, $attributes, $table, $exists);
}
...
The problem I am having is, while the active field is properly cast to boolean, the permissions field is not cast to array, instead the same string in the database is returned. I assure that the pivot table is properly setup and permissions column is MySQL TEXT column.
Currently I am using Laravel 5.1.16 (LTS).