Laravel 5 Eloquent Relationship - Has Many Though - eloquent

I have three models.
Study
Site
Unit
Study Has and belongs to many Sites and each site that belongs to Study again has and belongs to many Unit. Please see the following drawing.
http://tinypic.com/r/ojhx0g/8
How I achieve this using Laravel 5 Eloquent Relationships.

It sounds like you have many-to-many relationships between Study->Site and Site->Unit. You can read the Laravel documentation about many-to-many relationships here.
Models
Here are the relevant functions you'll need for Eloquent to recognize the relationships.
class Study extends Model {
// If you named your table differently (like 'studies'), specify that here
protected $table = 'studys';
// This assumes a pivot table named 'site_study', if you named yours
// differently you can pass in into the belongsToMany() function as the
// second parameter.
public function sites() {
return $this->belongsToMany('App\Site');
}
}
class Site extends Model {
protected $table = 'sites';
public function studies() {
return $this->belongsToMany('App\Study');
}
public function units() {
return $this->belongsToMany('App\Unit');
}
}
class Unit extends Model {
protected $table = 'units';
public function sites() {
return $this->belongsToMany('App\Site');
}
}
Then, to access the Sites belonging to a Study you would do this:
$sites = Study::find(1)->sites;
Pivot Table Migrations
Laravel expects pivot tables to be named like 'alpha_beta' where alpha and beta are the singular model names in alphabetical order. So your migrations for the pivot tables would look like this:
class CreateSiteStudyTable extends Migration {
public function up() {
Schema::create('site_study', function(Blueprint $table)) {
$table->integer('site_id')->unsigned();
$table->foreign('site_id')->references('id')->on('sites');
$table->integer('study_id')->unsigned();
$table->foreign('study_id')->references('id')->on('studys'); // or whatever you named it
$table->unique(['site_id', 'study_id']);
$table->timestamps();
});
}
public function down() {
Schema::drop('site_study');
}
}
class CreateSiteUnitTable extends Migration {
public function up() {
Schema::create('site_unit', function(Blueprint $table)) {
$table->integer('site_id')->unsigned();
$table->foreign('site_id')->references('id')->on('sites');
$table->integer('unit_id')->unsigned();
$table->foreign('unit_id')->references('id')->on('units');
$table->unique(['site_id', 'unit_id']);
$table->timestamps();
});
}
public function down() {
Schema::drop('site_unit');
}
}
You can read about Foreign Keys in Laravel here.

you have to create 3 models study, site and unit as per you diagram study has many sites and sites has many units, in you diagram study don't have direct relation with units your eloquent models will be like this.
class Study extends Model {
public function sites(){
return $this->hasMany('App\Site');
}
}
class Site extends Model {
public function units(){
return $this->hasMany('App\Unit');
}
public function study(){
return $this->belongsTo('App\Study');
}
}
class Unit extends Model {
public function sites(){
return $this->belongsTo('App\Site');
}
}

Related

Map property using OwnsOne method in EF Core 2.0

I'm trying to migrate EF 6 project to EF Core 2.0.
I would like to streess out that I am not allowed to change anything in database structure - must be exactly the same as for EF 6 project.
I have the foloowing entities:
abstract class Vehicle { ... }
abstract class Car : Vehicle
{
public Dimension Dimensions { get; set; }
}
class Audi : Car { ... }
class Mazda : Car { ... }
class Dimension
{
public double Width { get; set; }
public double Height { get; set; }
}
Table mappings as follows:
public VehicleMap(EntityTypeBuilder<Vehicle> entityBuilder)
{
entityBuilder.ToTable("Vehicles");
entityBuilder.HasKey(_ => _.Id);
entityBuilder.HasDiscriminator<string>("Type").HasValue<Truck>(nameof(Truck));
}
public CarMap(EntityTypeBuilder<Car> entityBuilder)
{
entityBuilder.HasDiscriminator<string>("Type")
.HasValue<Mazda>(nameof(Mazda))
.HasValue<Audi>(nameof(Audi));
**entityBuilder.OwnsOne(_ => _.Dimensions);**
}
The problem I have is about Dimensions property defined in Car abstract class. I would like to map it using OwnsOne method to have all its class properties defined in the same table.
I'm getting the following error:
Cannot use table 'Vehicles' for entity type 'Car.Dimensions#Dimension'
since it has a relationship to a derived entity type 'Car'. Either
point the relationship to the base type 'Vehicle' or map
'Car.Dimensions#Dimension' to a different table.
Any idea how to solve it in EF Core 2.0?
The Dimension known as ValueObject. The best practice for this type of objects is here.
To implement this objects in your context first you should define it in base table, since when you create database with this configuration you have just one table with the name of Vehicles and discriminator column with the name of Type.
As the error say:
Either point the relationship to the base type 'Vehicle'
we should define Dimension in base class.
abstract class Vehicle
{
public Dimension Dimensions { get; private set; }
}
Table mappings:
public VehicleMap(EntityTypeBuilder<Vehicle> entityBuilder)
{
entityBuilder.ToTable("Vehicles");
entityBuilder.HasKey(_ => _.Id);
entityBuilder.HasDiscriminator<string>("Type").HasValue<Truck>(nameof(Truck));
// Here
entityBuilder.OwnsOne(p => p.Dimensions);
}
I would recommend a base ConfigurationBuilder for your Car class. Than you can make a configuration class (http://www.entityframeworktutorial.net/code-first/move-configurations-to-seperate-class-in-code-first.aspx)
Than you can do something like this:
public class CarConfiguration<TBase> : IEntityTypeConfiguration<TBase>
where TBase : class, Car
{
public virtual void Configure(EntityTypeBuilder<TBase> builder)
{
builder.OwnsOne(e => e.Dimensions);
}
}
After that you can use a configurationClass that derives from CarConfiguration foreach item that derives from car e.g.
public class AudiConfiguration : CarConfiguration<Audi>
{
public override void Configure(EntityTypeBuilder<Audi> builder)
{
base.Configure(builder); //call this to use configuration from Car
builder.....
}
}

How to Query two foreign key, that belong to a table, that has two different relationships

I have been looking, but haven't found the solution.
In my laravel project, I have a table named players. Each player has a position_id and a team_id. Using relationship.
Here are my Models:
use Illuminate\Database\Eloquent\Model;
class Team extends Model
{
public function players()
{
return $this->hasMany('App\Player');
}
}
use Illuminate\Database\Eloquent\Model;
class Position extends Model
{
public function players()
{
return $this->hasMany('App\Player');
}
}
use Illuminate\Database\Eloquent\Model;
class Player extends Model
{
public function team()
{
return $this->belongsTo('App\Team');
}
public function position()
{
return $this->belongsTo('App\Position');
}
}
If I want all players with position==gk I make:
$players = Position::whereDescription('gk')->players;
If I want all player with team==pac I make:
$players = Team::whereDescription('pac')->players;
How do I query, all players from team==pac with position==gk?
Try this:
Player::where(['position_id'=>$positionId, 'team_id'=>$teamId])->first();
or
Player::where(['position_id'=>$positionId, 'team_id'=>$teamId])->get();
However, this code doesn't take advantage of Eloquent relations and assumes you have already have $postionId and $teamId.

Zend DB Table and Model one to one

I have class that representing model of user with foreign key with is id of picture .
class Model_User extends Model_AbstractEntity
{
protected $u_id;
protected $u_email;
protected $u_firstname;
protected $u_lastname;
protected $u_password;
protected $u_salt;
protected $u_created_at;
protected $u_updated_at;
protected $u_fb;
protected $u_status;
protected $u_r_id;
protected $u_p_id;
}
Class with is responsible for picture model look like this:
class Model_Picture extends Model_AbstractEntity
{
protected $p_id;
protected $p_created_at;
protected $p_updated_at;
protected $p_caption;
protected $p_name;
protected $p_basePath;
protected $p_available;
protected $p_u_id;
}
This is only model part with is getting data from database.
Foreing key is u_p_id and key in picture is p_id
My problem is that when doing select() by Zend db table it returning me data with foreign key but how can I know which part of return data is picture part to set the proper picture model.... how to do it in proper way no to do 2 queries one for user and second for picture to create 2 associative objects.
I'm talking now about relation ont to one but maybe will be one to many..
Typically your entity models will not exist in void they will exist in concert with some type of Data Mapper Model. The Mapper will typically be charged with gathering the data from whatever source is handy and then constructing the entity model.
For example I have a music collection that has an album entity:
<?php
class Music_Model_Album extends Model_Entity_Abstract implements Interface_Album
{
//id is supplied by Entity_Abstract
protected $name;
protected $art;
protected $year;
protected $artist; //alias of artist_id in Database Table, foreign key
protected $artistMapper = null;
/**
* Constructor, copied from Entity_Abstract
*/
//constructor is called in mapper to instantiate this model
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
}
}
/**
* Truncated for brevity.
* Doc blocks and normal getters and setters removed
*/
public function getArtist() {
//if $this->artist is set return
if (!is_null($this->artist) && $this->artist instanceof Music_Model_Artist) {
return $this->artist;
} else {
//set artist mapper if needed
if (!$this->artistMapper) {
$this->artistMapper = new Music_Model_Mapper_Artist();
}
//query the mapper for the artist table and get the artist entity model
return $this->artistMapper->findById($this->getReferenceId('artist'));
}
}
//set the artist id in the identity map
public function setArtist($artist) {
//artist id is sent to identity map. Can be called later if needed - lazy load
$this->setReferenceId('artist', $artist);
return $this;
}
//each album will have multiple tracks, this method allows retrieval as required.
public function getTracks() {
//query mapper for music track table to get tracks from this album
$mapper = new Music_Model_Mapper_Track();
$tracks = $mapper->findByColumn('album_id', $this->id, 'track ASC');
return $tracks;
}
}
In the mapper I would build the entity model like:
//excerpt from Model_Mapper_Album
//createEntity() is declared abstract in Model_Mapper_Abstract
public function createEntity($row)
{
$data = array(
'id' => $row->id,
'name' => $row->name,
'art' => $row->art,
'year' => $row->year,
'artist' => $row->artist_id,//
);
return new Music_Model_Album($data);
}
to use this method in a mapper method, might look like:
//this is actually from Model_Mapper_Abstract, b ut give the correct idea and will work in any of my mappers.
//this returns one and only one entity
public function findById($id)
{
//if entity id exists in the identity map
if ($this->getMap($id)) {
return $this->getMap($id);
}
//create select object
$select = $this->getGateway()->select();
$select->where('id = ?', $id);
//fetch the data
$row = $this->getGateway()->fetchRow($select);
//create the entity object
$entity = $this->createEntity($row);
//put it in the map, just in case we need it again
$this->setMap($row->id, $entity);
// return the entity
return $entity;
}
I have seen Entities and Mappers built in many different ways, find the method that you like and have fun.
A lot of code has been left out of this demonstration as it doesn't really apply to the question. If you need to see the complete code see it at GitHub.

MVC2, Entity Framework, & repository pattern

I'm trying to get a repository pattern working with MVC2 and EF.
My problem is within the concrete repository. When I attempt to cast the EF query results as an IEnumerable collection of view-model entities:
Unable to cast object of type
'System.Data.Objects.ObjectQuery`1[Data_Service.MediaReleases]'
to type
'System.Collections.Generic.IEnumerable`1[TestMVCWithFacory.Models.Entities.MediaReleaseModel]'.
I sense that's a bone-headed thing to try to do -- and it's something with Linq, and how deferred execution works, but I don't really understand the voodoo.
So what is it that I'm mis-understanding there, and how do I address it?
The view-model:
public class MediaReleaseModel
{
public string Headline { get; set; }
public string FullText { get; set; }
}
The repository interface:
public interface IMediaReleasesRepository
{
IEnumerable<MediaReleaseModel> MediaReleases { get;}
}
The concrete repository:
public class MediaReleaseRepository : IMediaReleasesRepository
{
private NewsEntities DataContext = new NewsEntities();
private IEnumerable<MediaReleases> _MRs;
public MediaReleaseRepository()
{
_MRs = from art in DataContext.MediaReleases select art;
}
public IEnumerable<MediaReleaseModel> MediaReleases
{
get { return (IEnumerable<MediaReleaseModel>)_MRs; }
}
}
Controller:
public class HomeController : Controller
{
private IMediaReleasesRepository _MRRepository;
public HomeController()
{
_MRRepository= new MediaReleaseRepository();
}
public ViewResult index()
{
return View(_MRRepository.MediaReleases.ToList());
}
}
You're trying to cast collection of MediaReleases to collection of MediaReleaseModels. If MediaReleaseModel is a separate class, this can't be done just by casting. Generally, cast will succeed only in one inheritance chain or when conversion operators are defined, which is not the case here.
What you need here is rewriting the MediaRelease fields to you model object (it can be automated using tools like AutoMapper), i.e. with help of LINQ:
public IEnumerable<MediaReleaseModel> MediaReleases
{
get
{
return _MRs.Select(x => new MediaReleaseModel()
{
Prop1 = x.Prop1
/* etc. */
});
}
}
One suggestion at the side: it's better not to have logic like that in constructor, creating objects should be cheap operation and it's a bit strange when the data are fetched before they are really needed.

select queries with Zend_DB_Table

I have a code something like following
class Application_Model_Company extends Zend_Db_Table_Abstract {
protected $_name = 'companies';
private $id;
private $name;
private $shortName;
private $description;
public static function getAllCompanies() {
$companyObj = new self();
$select = $companyObj->select()->order(array('name'));
$rows = $companyObj->fetchAll($select);
if($rows) {
$companies = array();
foreach($rows as $row) {
$company = new self();
$company->id = $row->id;
$company->name = $row->name;
$company->shortName = $row->short_name;
$company->description = $row->description;
$companies[] = $comapny;
}
// return Company Objects
return $companies;
}else
throw new Exception('Oops..');
}
}
I need to return Company Objects from getAllCompanies() function, But it returns Zend_Db_Table_Row Object. How do I correct this?
Your Model class shouldnt extend the table class. The table class is separate. Your Model should extend the row class if extending anything from Zend_Db at all. Also you shouldnt put retrieval methods on your Model classes directly, they would go on the table classes.
This is because in the paradigm youre trying to use here, a Model represents a single Row of data, the Table class represents the table as a repository of data, and the Rowset class represents a collection of Rows (or Models).
To properly implement what you are describing in your question you would do something like the following:
class Application_Model_DbTable_Company extends Zend_Db_Table_Abstract
{
// table name
protected $_name = 'company';
protected _$rowClass = 'Application_Model_Company';
// your custom retrieval methods
}
class Application_Model_Company extends Zend_Db_Table_Row
{
protected $_tableClass = 'Application_Model_DbTable_Company';
// custom accessors and mutators
}
However, using some kind of implementation of the Data Mapper pattern is whats actually recommended. Check out the Quickstart for a thorough tutorial on a simplified implementation.