Laravel 5.2 eloquent relationship eager fetching - eloquent

I have extended my user model to have a role, the tables are as follows
users(id,name,email,password,remember_token, role_id,created_at,updated_at)
roles(id,name,description,created_at,updated_at)
then the User model is
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function role()
{
return $this->hasOne('App\Models\Role', 'id', 'role_id');
}
}
and the Role model is
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model {
protected $table = 'roles';
public function users()
{
return $this->hasMany('App\User', 'role_id', 'id');
}
}
I am returning the Users as json and i want them to eager fetch the role using query builder
$users = DB::table('users')->skip($skip)->take($size)->get();
the output i am getting is
{"id":1,"name":"Vinnie Ashken","email":"ashken#eml.cc","password":"$2y$10$fsSxekoktUV5xqe02hMZIuWVRpykmMvjGa.AJUriCEX.KPRfj.Yk.","role_id":1,"remember_token":"184J9pVLf9N2yQ5YfD9Yf5d88uavn0dCFGsAaXnamXKiIbLugIKAVJTmS1t6","created_at":"2016-03-16 00:23:56","updated_at":"2016-03-16 00:42:56"}
what i want is
{"id":1,"name":"Vinnie Ashken","email":"ashken#eml.cc","password":"$2y$10$fsSxekoktUV5xqe02hMZIuWVRpykmMvjGa.AJUriCEX.KPRfj.Yk.","role":{"id":1, "name":"Administrator","description":"test"},"remember_token":"184J9pVLf9N2yQ5YfD9Yf5d88uavn0dCFGsAaXnamXKiIbLugIKAVJTmS1t6","created_at":"2016-03-16 00:23:56","updated_at":"2016-03-16 00:42:56"}

Try using the full Eloquent model (User::) instead of the bare query builder (DB::table('users')). Then you'll be able to use with and other Relationship methods (in addition to all of the query builder methods):
$users = App\User::with('role')->skip($skip)->take($size)->get();

Related

Symfony 4: creating form for an entity that has relationships with multiple entities

I'm trying make a site that let users custom build a car. A new instance of car model will be created when a user started. In the car model, the user can add different parts and define custom color to it. And the name of the parts will be loaded a table that stores all the names. So it will look like 'belt -> brown', 'left-door -> red'.
Right now I have finish creating the entities and relationship. They are the CarModel, ModelPart and PartName entities:
CarModel entity
class CarModel
{
private $id;
/**
* #ORM\ManyToMany(targetEntity="ModelPart", inversedBy="carModels")
*/
private $modelParts;
}
ModelPart entity
class ModelPart
{
private $id;
/**
* #ORM\ManyToMany(targetEntity="CarModel", mappedBy="modelParts")
*/
private $carModels;
/**
* #ORM\OneToOne(targetEntity="PartName")
*/
private $partName;
// #var string
private $color;
}
PartName entity
class PartName
{
private $id;
private $name;
private $modelPart
}
But I couldn't figure out enter them from a form.
CarModelType form
class CarModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('part_name', EntityType::class, [
'class' => partName::class,
'choice_label' => 'name'
])
->add('color', TextType::class)
;
}
I got the error message:
Neither the property "part_name" nor one of the methods "getPartName()", "partName()", "isPartName()", "hasPartName()", "__get()" exist and have public access in class "App\Entity\CarModel".

Form setter option Symfony 3

I'm using Symfony 3 to build a website. I have an Entity (Users) that is in OneToOne relation with itself in order to make couples. (I didn't have others idea on how to do it easily)
The end goal is to create a form to reference the id of the other Users in the couple. So I created an IntegerType field and assign it the id but I can't set it (because there are no setId(...)). So I would know if there is a setter option (can't find in Doc/Tests), and if there isn't how could I achieve this ?
The steps to register a new couple would have been:
Send new id (of the other Users) [FORM]
Fetch the other Users ($userCouple = ...findOne...) [BDD]
If he have $couple == null then $userCouple->setCouple($this) and $this->setCouple($userCouple)
So my Users entity looks like:
<?php
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="Acme\UserBundle\Repository\UsersRepository")
*/
class Users extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Acme\UserBundle\Entity\Users")
* #ORM\JoinColumn(nullable=true)
*/
protected $couple;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set couple.
*
* #param \Acme\UserBundle\Entity\Users|null $couple
*
* #return Users
*/
public function setCouple(\Acme\UserBundle\Entity\Users $couple = null)
{
$this->couple = $couple;
return $this;
}
/**
* Get couple.
*
* #return \Acme\UserBundle\Entity\Users|null
*/
public function getCouple()
{
return $this->couple;
}
}
And my form looks like :
<?php
namespace Acme\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
class ProfileFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('couple', IntegerType::class, array(
'label' => 'Couple ID',
'property_path' => 'couple.id',
'attr' => array('min' => 0),
));
}
public function getBlockPrefix()
{
return 'acme_user_profile';
}
}
You should solve this using a one-to-one self-referencing relation (see more here). Basically your couple would be replaced by partner which suites best the case:
...
/**
* #OneToOne(targetEntity="User")
* #JoinColumn(name="partner_id", referencedColumnName="id")
*/
protected $partner;
...
Then in the form you could use the EntityType (not sure why you wanted to use IntegerType in the first place) and do something like this:
$builder->add('users', EntityType::class, array(
// query choices from this entity
'class' => 'UserBundle:User',
// use the User.username property as the visible option string
'choice_label' => 'username',
));
Of course you can exclude the user you're editing the profile from the list of users you show as possible partners using query_builder option (passing a custom query) or choices to pass the collection of User entities you want to use (getting them first and filter out current user).

Laravel relationship with a pivot table

I have 3 tables:
Category ( id, name)
Category_Tournament (category_id, tournament_id) --> pivot table
Category_Tournament_User (category_id, tournament_id, user_id, confirmed)
Category is the list of available categories
Category_Tournament is the list of categories the admin configured
Category_tournament_User is the categories the user has registred
To get all categories in the tournament, I can do it easily with:
tournament->categories
defining a belongsToMany relationship in tournament model
What I don't know how to define relationship with the last table.
What I need is the user click on several categories, and I can run something like:
tournament->user_categories->sync($userCategories)
where I should sync table Category_Tournament_User ( with category_id, tournament_id, user_id)
What is the best way to achieve it???
EDIT:
Model Tournament:
class Tournament extends Model
{
protected $table = 'tournament';
public $timestamps = true;
protected $fillable = [
'name',
'date',
'type',
];
/**
* A tournament is owned by a user
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
return $this->belongsTo('App\User', 'user_id','id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categories()
{
return $this->belongsToMany('App\Category')
->withTimestamps();
}
}
Model Category
class Category extends Model
{
protected $table = 'category';
public $timestamps = true;
protected $fillable = [
'id',
'name',
];
public function tournaments()
{
return $this->belongsToMany('App\Tournament');
}
}
Model User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, HasRole;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name','firstname','lastname','email', 'password','avatar',country_id','role_id',,'provider','provider_id','verified'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($user) {
$user->token = str_random(30);
});
}
public function role()
{
return $this->belongsTo('App\Role');
}
public function settings()
{
return $this->hasOne('App\Settings');
}
public function invites()
{
return $this->hasMany('App\Invite', 'email','email');
}
public function country()
{
return $this->belongsTo('Webpatser\Countries\Countries');
}
/**
* A user can have many tournaments
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tournaments()
{
return $this->hasMany('App\Tournament');
}
}
You have many to many relationship here between User and Category_Tournament and you should take a look in documentation exactly at Many To Many.
I think you don't need to to have Category_Tournament_User table. and you can't make a Model for it in Laravel. you only need to a table user_tournament. and you should define relation(foreign key) on migration, like this:
Schema::create('user_tournament', function(Blueprint $table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('tournament_id')->unsigned();
$table->unique(['tournament_id', 'user_id']);//You can omit this
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('tournament_id')->references('id')->on('tournaments')->onDelete('cascade')->onUpdate('cascade');
$table->nullableTimestamps();
});
then you can use this code:
user->tournaments->sync($userCategories);

Joining-Table with Metadata Impairs Getters/Setters - Doctrine 2

I'm writing a feature which calls for the records of my joining table to carry extra metadata (Joining-Table with Metadata). I've attempted to implement this in accordance with this section of the Doctrine documentation.
See below for example Entity definitions.
The challenge now is that getGroups and setGroups do not yield/set Group entities (& the same is true from the Group instance perspective), but they yield GroupUser entities.
This adds a substantial delay to process of managing this relationships, which so far have been extremely smooth - for example, I cannot simply add, remove, or check for the existence of a Group to the collection which getGroups yields.
Can anyone identity any errors in my implementation, or else recommend a more fluid way of implementing this concept?
Thanks in advance for any input.
EDIT:
My main concern is this: using this implementation, retrieving a collection of Users from a Group entity requires this Entity method's mediation:
public function getUsers() {
return $this->users->map(function($groupUser){
return $groupUser->getUser();
});
}
I'm concerned that this could imply a major performance hit down the road. Am I incorrect?
Furthermore, how does one re-implement the setUsers method?
Group entity:
<?php
/**
* #Entity
* #Table(name="group")
*/
class Group {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="group")
* #var \Doctrine\Common\Collections\Collection
*/
protected $users;
}
User entity:
<?php
/**
* #Entity
* #Table(name="user")
*/
class User {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="user")
* #var \Doctrine\Common\Collections\Collection
*/
protected $groups;
}
Joining entity:
<?php
/**
* #Entity
* #Table(name="group_user")
*/
class GroupUser {
/**
* #Id
* #ManyToOne(targetEntity="User", inversedBy="groups")
* #JoinColumn(name="userId", referencedColumnName="id")
*/
protected $user;
/**
* #Id
* #ManyToOne(targetEntity="Group", inversedBy="users")
* #JoinColumn(name="groupId", referencedColumnName="id")
*/
protected $group;
/**
* #Column(type="integer")
*/
protected $relationship;
}
Related -
Same goal, slightly different approach, which consistently produced errors once the resulting collections were manipulated: http://www.doctrine-project.org/jira/browse/DDC-1323
Supports the approach, no technical details: Doctrine 2 join table + extra fields
I've found just two examples (see question) of entity definitions for this specific type of relationship, however no example code for how they're used. As such it was fairly unclear how fluid (or otherwise) the resulting setters & getters could be expected to be. Hopefully this code will help clear up the approach for anyone else making a similar attempt.
The ideal solution under the circumstances (thanks #doctrine # freenode) was to implement a custom repository - a more flexible & efficient place for creating & managing the association.
Example Custom Repository for Join-Table with Metadata Class - Solution accompanies code in original question
<?php
use Doctrine\ORM\EntityRepository;
class GroupUserRepository extends EntityRepository {
/**
* #param \User $user
* #param \Group $group
* #param integer $type One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUserToGroup(User $user, Group $group, $relationship, $role = '') {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if(!$groupUser) {
$groupUser = new GroupUser();
$groupUser->setGroup($group);
$groupUser->setUser($user);
$groupUser->setRole($role);
$groupUser->setRelationship($relationship);
$this->_em->persist($groupUser);
}
return $groupUser;
}
/**
* #param \User $user
* #param \Group $group
* #return null
*/
public function removeUserFromGroup(User $user, Group $group) {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if($groupUser)
$this->_em->remove($groupUser);
}
}
Then, from the join-table class, modify the Entity meta-data accordingly to specify the custom repository.
<?php
/**
* #Entity(repositoryClass="\Path\To\GroupUserRepository")
*/
class GroupUser {
// ...
}
This causes the custom repository to yield in place of the default one, making a proxy method from the Entity class simple.
<?php
/**
* #Entity
*/
class Group {
/**
* #param \User $user
* #param integer $relationship One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUser(User $user, $relationship, $role = '') {
return $this->_em->getRepository('GroupUser')
->addUserToGroup($user, $this, $relationship, $role);
}
}
And things are about as manageable as they were before.

Zend Framework - Cascading Deleting Using Table Data Mapper pattern

My problem getting Zend Framework to provide a DRI layer can now be summarized as such.
Using the class definitions below I am able to delete the user but not the related comment through my local UserController "public/users/delete/userId/22", even though I have set up a refernece map and table relationship definition.
Does anyone have any answers to why the associated comment record is not deleted when i delete the users record?
class Default_Model_DbTable_Comment extends Zend_Db_Table_Abstract
{
/**
* #var string Name of the database table
*/
protected $_name = 'comment';
/**
* #desc reference map
*
* Rows in the comment table are to be automatically deleted if the row in the
* User table to which they refer is deleted
*
*/
protected $_referenceMap = array(
'User' => array(
'columns' => 'user_id', // the foreign key(s)
'refTableClass' => 'Default_Model_DbTable_Users',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
)
);
}
class Default_Model_DbTable_Users extends Zend_Db_Table_Abstract
{
/**
* #var string Name of the database table
*/
protected $_name = 'users';
/**
* #desc Defining referential integrity here since we are using MyISAM
* Dependent tables are referred via the class name.
*/
protected $_dependentTables = 'Default_Model_DbTable_Comment';
}
I've created models as yours and on testing it appears that it only works if dependent tables are listed in an array:
class Default_Model_DbTable_Users extends Zend_Db_Table_Abstract
{
/**
* #var string Name of the database table
*/
protected $_name = 'users';
/**
* #desc Defining referential integrity here since we are using MyISAM
* Dependent tables are referred via the class name.
*/
protected $_dependentTables = array('Default_Model_DbTable_Comment');
}
When they aren't listed in an array, I get the error
Warning: Invalid argument supplied for foreach() in C:\PHP\includes\ZendFramework-1.8.4-minimal\library\Zend\Db\Table\Row\Abstract.php on line 632
This error may not have been visible in your environment.