Laravel 5.1.* Custom Pivot Class Array Not Casted - mysql

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).

Related

Laravel API resources, get only the latest occurrence from a collection

Hi I am developing an api in laravel for an online course system. In this scheme I have a standard table for users, a table for courses and a pivot table that relates courses and users according to which they sign up for each course.
This last table also carries the events related to the progress of each user in the course, that is, Subscribed, Progress x%, Completed, Approved, so that each user can have multiple entries in the course_users table.
So far everything is clear and everything is fine, the point is that at a certain moment I need to return a json object with the information of the courses and pointed users, this can be clearly achieved using resource collection in the following way:
CourseCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
use App\Http\Resources\CargoResource;
class CourseCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'data' => CourseResource::collection($this->collection),
'links' => [
'self' => 'link-value',
],
];
}
}
CourseResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class CourseResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title'=> $this->title,
'description'=> $this->description,
'price'=> $this->price,
'users' => CourseUserResource::collection($this->whenLoaded('users'))
];
}
}
CourseUserResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class CourseUserResource extends JsonResource
{
public function toArray($request)
{
return [
'course_id'=> $this-> course_id,
'user_id'=> $this->user_id,
'event'=> $this->event,
'event_date' => $this->created_at->format('Y-m-d')
];
}
}
The problem to be solved is that with this scheme I obtain a collection of events for each user and course, but what I am needing is only the last event of each user, to know what their status is in relation to the course.
I am analyzing the option to perform the query by sql and then manually build the json object, but I would like to have a "laravel style" solution
Any ideas will be welcome!
Added Models & Controllers for clarification
class Course extends Model
{
protected $fillable = [
'title',
'slug',
'description',
'course_category_id',
'price',
'published'
];
...
public function history()
{
return $this->belongsToMany(CourseUser::class, 'course_id', 'id')->latest();
}
public function scopePublished($query)
{
return $query->where('published', 1);
}
}
class CourseUser extends Model
{
protected $fillable = [
'course_id',
'user_id',
'event'
];
}
class SearchController extends ApiController
{
public function search(Request $request)
{
$results = Course::with('history')
->published
->where('title', 'like', $request->filter['title'])
->where('description', 'like', $request->filter['description'])
->get();
if (! count($results) > 0) {
return $this->sendResponse(
__('No results for your query.'),
[
'code'=>204,
'message'=> __('There are no results for your search criteria.')
],
204
);
}
return new CourseCollection($results);
}
}

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'),
];
}

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

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
]);

Yii2, Model find() with custom attribute

I want to pull the model data with custom attribute that assigned in a function in model.
Example)
class Test extends ActiveRecord
{
public static function tableName()
{
return '{{%test}}';
}
public function rules()
{
//....
}
public function attributeLabels()
{
return [
'id' => 'ID',
'first_name' => 'First Name',
'last_name' => 'Last Name',
];
}
public function getFullName()
{
$fullName = $this->first_name.' '.$this->last_name;
return $fullName;
}
}
Test::find().with('fullName') => it doesn't work
How can I get all the data with fullname attribute?
with is for relations. You can get fullname attribute just by calling $model->fullName. Actually fullName is not an attribute, yii2 utilise php's magic method __get() to get it from getFullName() method.
Example:
$model = Test::findOne($id);
echo $model->fullName;
Example 2:
$models = Test::find()->all();
foreach($models as $model)
{
echo $model->fullName;
}
Also consider using of fields/extraFields methods if you want use your models as arrays instead of objects

action Update() in Yii to update another model's update

I have the database like this
=== Invoice ===
id
customer_id (FK)
description
=== Customer ===
id
firstname
lastname
I have multimodel for both the form so that Cstomer table will be load in Invoice. So that I can easily access the two models from a single view. For that I have made relation in both models just like this
In Invoice model the realtion is like this
public function relations()
{
return array(
'customer' => array(self::BELONGS_TO,'Customer','customer_id'),
);
}
In Customer Model the relation is like this
public function relations()
{
return array(
'invoice' => array(self::HAS_MANY, 'Invoices','customer_id')
);
}
Everything is working fine.But when I am going for actionUpdate() in Invoice controller file there is Customer model is not defined. So I made it define like this
public function actionView($id)
{
$this->render('view',array(
'model'=>$this->loadModel($id),
'customers'=>Customers::model()->findByPk(array('customer_id'=>$_GET['id']));
));
}
It is showing as Undefined offset: 0. I want here in ('customer_id'=>$_GET['id']) the value of id so that I can easily show and update the values for each ids.
If I am giving the value like this
public function actionView($id)
{
$this->render('view',array(
'model'=>$this->loadModel($id),
'customers'=>Customers::model()->findByPk(28);
));
}
It is easily showing the value from Customer id. So how to get those values?Any help and suggestions will be highly appriciable.
Try this
public function actionView($id)
{
$model = $this->loadModel($id);
$this->render('view',array(
'model'=>$model,
'customers'=>Customers::model()->findByPk($model->customer_id);
));
}