I'm new to zend framework, i'm trying to understand how table relationships work. I have two tables and i'm trying to link them and get their data in a list.
CREATE TABLE `relationship` (
`relationship_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`relationship_name` varchar(45) NOT NULL,
`relationship_group_id` int(10) unsigned NOT NULL,
`display` int(10) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`relationship_id`),
KEY `FK_relationship_1` (`relationship_group_id`),
CONSTRAINT `FK_relationship_1` FOREIGN KEY (`relationship_group_id`) REFERENCES `relationship_group` (`relationship_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `relationship_group` (
`relationship_group_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`relationship_group_name` varchar(45) NOT NULL,
`display` int(10) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`relationship_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In my relationship table class, I have:
class Relationship_Table extends Zend_Db_Table_Abstract
{
protected $_rowClass = 'Relationship';
protected $_name = 'relationship';
In my relationship group table class I have:
class Relationship_Group_Table extends Zend_Db_Table_Abstract
{
protected $_name = 'relationship_group';
protected $_rowClass = ' Relationship_Group';
I am not sure what my $_referenceMap and $_dependentTables should say, and if I need to state them in both classes or just one?
Also how do I get a list from my relationships table with the corresponding relationship_group data included.
Any help is appreciated.
Here is a pretty good primer on table relationships.
Mat McCormisck on Table relationships in Zend Framework
The actual answer to your question is:
It depends on what you need to accomplish and how do you want to accomplish it.
$_dependentTables aren't required in your case (using InnonDB).
Zend References
Note: Skip declaration of $_dependentTables if you use referential integrity constraints in the RDBMS server to implement cascading operations
Your $_referenceMap should link FOREIGN KEY in a dependent table to the PRIMARY KEY in the parent table and it's only required in the dependent table.
The rest is as RockyFord suggested in his link :).
Related
I need to use my Website model to get a row from my database within the websites table, however this row is identified through my domains table.
So basically it would be great to do a query on my domains table and match the row, then from that get the website row from the websites table using the website_id column.
But I want to simply pass this data into my controller by just referencing the Model within the method.
class WebsiteController extends Controller {
public function index(Website $website) {
print_r($website);
return view('index');
}
}
My domains table:
CREATE TABLE `domains` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`website_id` INT(11) NOT NULL DEFAULT '0',
`domain` VARCHAR(255) NOT NULL DEFAULT '0',
`active` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
INDEX `website_id` (`website_id`),
CONSTRAINT `website_id` FOREIGN KEY (`website_id`) REFERENCES `websites` (`id`)
)
COMMENT='This table will contain all of the domains registered on MarvWeb, this will link to the website record. '
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3;
And websites table:
CREATE TABLE `websites` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
`tagline` VARCHAR(255) NULL DEFAULT NULL,
`description` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COMMENT='This table will contain all the websites data. '
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2;
Does this make sense?
Add a website function to your Domain model.
class Domain extends Model{
public function website(){
return $this->hasOne('App\Website');
}
// remainder of model.
}
When you retrieve the Domain query results, the website can be accessed by
print_r($domainRowResult->$website->tagline);
I'd like to use Laravel Eloquent Polymorphic Relationships however it doesn't seem to be setup to work with my table structure.
Essentially I have a gamedata table which includes all the different types of gamedata (nation, league, team, player etc). For each type I have multiple tables with information separated by game_id. So there would be one row for the nation "England" in the gamedata table, which has 7 corresponding rows in the nations table with data from 7 different game_ids.
I'd like to be able to select some rows from the gamedata table and their corresponding rows from the appropriate table depending on it's type.
This is easy enough to do on a one to one relationship, but seems impossible to do with a one to many relationship.
Here is the gamedata table.
CREATE TABLE `gamedata` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data_type` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `data_id` (`data_id`,`type`),
FULLTEXT KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
And then lots of tables like this (lots of columns removed for ease of reading):
CREATE TABLE `nations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `leagues` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `teams` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
So some rows on the gamedata table might look like this:
(144, 'nation', 'Some Nation'),
(145, 'nation', 'Another Nation'),
(146, 'league', 'Some League'),
(147, 'league', 'Another League'),
(148, 'team', 'Some Team'),
(149, 'team', 'Another Team');
So I should be able to do a polymorphic relationship from the "data_type" column and the "data_id" column to get the corresponding row from the appropriate table.
But none of the built in relationships (morphTo, morphMany, morphedByMany) etc seem to be able to handle it.
It seems like what I want is the morphTo() relationship but it seems to restrict itself to only returning one related model. All the relationships that accept multiple models require a specific model to be defined.
// This would work fine if I only wanted one related model. "data_type" being the class and "id" corresponding to "gamedata_id" on relevent table.
$this->morphTo('data');
// These require me to be explicit about the class instantiating rather than using from the "data_type" column
$this->morphMany(???, 'data');
$this->morphToMany(???, 'data');
$this->morphedByMany(???, 'data');
Is there a way to do this using the existing Laravel Relationships? Or is there a simple way to create my own relationship class based on morphTo that would suit my needs?
I think i've come up with a custom solution by extending the morphTo class.
namespace App;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* #mixin \Illuminate\Database\Eloquent\Builder
*/
class MorphHasMany extends MorphTo
{
/**
* Get the results of the relationship.
*
* #return mixed
*/
public function getResults()
{
return $this->ownerKey ? $this->query->get() : null;
}
/**
* Match the results for a given type to their parents.
*
* #param string $type
* #param \Illuminate\Database\Eloquent\Collection $results
* #return void
*/
protected function matchToMorphParents($type, Collection $results)
{
$ownerKeyName = $this->ownerKey ?: $results->first()->getKeyName();
foreach ($results->groupBy($ownerKeyName) as $ownerKey => $result) {
if (isset($this->dictionary[$type][$ownerKey])) {
foreach ($this->dictionary[$type][$ownerKey] as $model) {
$model->setRelation($this->relation, $result);
}
}
}
}
}
The morphTo() class already returns a Collection of results, but either uses first() or multiple instances of setRelation() to mean there is only one set. By overloading getResults() and matchToMorphParents() I can modify this behaviour to allow setting of a Collection instead.
In order to define the relationship i'll need a custom morphHasMany() method. This could be added to a base Model.php that extends Eloquent\Model.
/**
* Define a polymorphic has many relationship.
*
* #param string $name
* #param string $type
* #param string $id
* #param string $ownerKey
* #return MorphHasMany
*/
public function morphHasMany($name = null, $type = null, $id = null, $ownerKey = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
$name = $name ?: $this->guessBelongsToRelation();
list($type, $id) = $this->getMorphs(
Str::snake($name), $type, $id
);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. In this case we'll just pass in a dummy query where we
// need to remove any eager loads that may already be defined on a model.
if (empty($class = $this->{$type})) {
return new MorphHasMany($this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name);
} else {
$instance = $this->newRelatedInstance(
static::getActualClassNameForMorph($class)
);
return new MorphHasMany($instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name);
}
}
Then simply define it just like the usual morphTo() method.
public function data()
{
return $this->morphHasMany();
}
Or in my case:
public function data()
{
return $this->morphHasMany('data', 'data_type', 'id', 'gamedata_id');
}
So far no problems, but of course I may run into some in the future.
Hi I have these two tables that I want to join using relations in Yii, The problem is Im having a hard time figuring out how Yii relation works.
picturepost
id
title
link_stat_id
linkstat
id
link
post_count
I also have a working SQL query. This is the query I want my relation to result when I search when I want to get picturepost
SELECT picturepost.id, picturepost.title,linkstat.post_count
FROM picturepost
RIGHT JOIN linkstat
ON picturepost.link_stat_id=linkstat.link;
I want something like this when I search for a post.
$post = PicturePost::model() -> findByPk($id);
echo $post->linkCount;
Here's my table for extra info:
CREATE TABLE IF NOT EXISTS `picturepost` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci DEFAULT NULL,
`link_stat_id` char(64) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM;
CREATE TABLE IF NOT EXISTS `linkstat` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`link` char(64) COLLATE utf8_unicode_ci NOT NULL,
`post_count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `post_count` (`post_count`),
KEY `link_stat_id` (`link`)
) ENGINE=InnoDB;
Thanks in advance I hope I explained it clearly.
There are a few tutorial regarding this, and I won't repeat them, but urge you to check them out.
The easiest starting point will be to create your foreign key constraints in the database, then use the Gii tool to generate the code for the model, in this case for the table picturepost.
This should result in a class Picturepost with a method relations(),
class Picturepost extends {
public function relations()
{
return array(
'picturepost_linkstats' => array(self::HAS_MANY,
'linkstat', 'link_stat_id'),
);
}
This links the 2 tables using the *link_stat_id* field as the foreign key (to the primary key of the linked table).
When you are querying the table picturepost, you can automatically pull in the linkstat records.
// Get the picturepost entry
$picturepost = PicturePost::model()->findByPk(1);
// picturepost_linkstats is the relationship name
$linkstats_records = $picturepost->picturepost_linkstats;
public function relations()
{
return array(
'linkstat' => array(self::HAS_ONE, 'Linkstat', array('link_stat_id'=>'link')),
);
}
More on yii relations.
This assumes that you have an active record model Linkstat that represents data in table linkstat.
I'm using Datamapper ORM for the first time together with the array and htmlforms extensions for CRUD. So far both extensions worked well for all my models but today I'm facing a weird problem for one model.
When I try to save an object I get this SQL error:
A Database Error Occurred
Error Number: 1054
Unknown Column 'orderitems.orderitem_id' in where clause
SELECT COUNT(*) AS `numrows` FROM (`orderitems`) WHERE `orderitems`.`order_id` = 2 AND `orderitems`.`orderitem_id` NOT IN (8, 9)
Filename: libraries/datamapper.php
Line Number: 2526
I think the correct SQL should be ...AND orderitems.id NOT IN (...) becasue the Orderitem model is referencing itself and not another related model. This error is triggered inside the count() method which is triggered by the save() method which is triggered by the from_array() method of the array DM extension.
Here is my controller code
//Get order
$order = new Order(2);
//Fields to render in the form
$fields = array('orderitem');
//Save changes from $_POST
if($post = $this->input->post())
{
$order->trans_begin();
if ( ! $order->from_array($post, $fields, TRUE) OR $order->trans_status() === FALSE)
$order->trans_rollback();
else
$order->trans_commit();
}
//Load view
$data = array(
'order' => $order,
'fields'=> $fields,
);
$this->load->view('order/edit', $data);
Here is my view code
echo $order->render_form($fields);
And here are the implied models
class Order extends DataMapper {
public $has_one = array('orderstatus', 'place', 'user', 'paymenttype');
public $has_many = array('orderitem');
...
/*SQL:
CREATE TABLE IF NOT EXISTS orders (
id mediumint(4) unsigned NOT NULL auto_increment PRIMARY KEY,
user_id mediumint(1) unsigned, FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT,
orderstatus_id tinyint(1) unsigned, FOREIGN KEY (orderstatus_id) REFERENCES orderstatuses(id) ON UPDATE CASCADE ON DELETE RESTRICT,
place_id tinyint(1) unsigned, FOREIGN KEY (place_id) REFERENCES places(id) ON UPDATE CASCADE ON DELETE SET NULL,
paymenttype_id tinyint(1) unsigned, FOREIGN KEY (paymenttype_id) REFERENCES paymenttypes(id) ON UPDATE CASCADE ON DELETE SET NULL,
total float NOT NULL DEFAULT 0
) ENGINE = InnoDB;
*/
class Orderitem extends DataMapper {
public $has_one = array('order', 'orderitemstatus');
public $has_many = array('orderitemextra');
...
/*SQL:
CREATE TABLE IF NOT EXISTS orderitems (
id int(1) unsigned NOT NULL auto_increment PRIMARY KEY,
order_id mediumint(1) unsigned, FOREIGN KEY (order_id) REFERENCES orders(id) ON UPDATE CASCADE ON DELETE CASCADE,
orderitemstatus_id tinyint(1) unsigned, FOREIGN KEY (orderitemstatus_id) REFERENCES orderitemstatuses(id) ON UPDATE CASCADE ON DELETE RESTRICT,
name varchar(128) NOT NULL,
price float NOT NULL,
) ENGINE = InnoDB;
*/
As I said I had no problems with other models so I don't know if it's a DM problem or a problem with my code or any of my models definitions.
I'll appreciate any kind of help.
Thanks!
When programming OO in PHP i never know exactly how to map a class to simple lists of data. I will try to make a simple example wich i am running into every day:
First the MySQL table creates:
/* create product table */
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci,
`price` decimal(10,0) NOT NULL,
`brand` int(11) NOT NULL,
`deliverytime` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/* data list for all kind of brands */
CREATE TABLE `brand` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`brand` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/* brand data*/
insert into `brand`(`id`,`brand`) values (1,'nike'),(2,'adidas'),(3,'diesel'), (4,'dkny'),(5,'lacoste');
/* data list for deliverytime */
CREATE TABLE `deliverytime` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deliverytime` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/* deliverytime data */
insert into `deliverytime`(`id`,`deliverytime`) values (1,'1 day'),(2,'2 days'),(3,'3 days'),(4,'4 days'),(5,'5 days'),(6,'6 days'),(7,'1 week'),(8,'1 - 2 weeks'),(9,'2 - 3 weeks'),(10,'4 - 5 weeks');
Then i create the product class.
class Product{
private
$name,
$description,
$price,
$brand
$deliverytime;
public function __construct(){
// etc etc
}
public function save(){
// save the product
}
}
Now the big question(s) are:
How should i handle $brand and $deliverytime in my Product class?
Should i make a Brand and DeliveryTime object (wich in turn are responsible for fetching the right brand and or deliverytime data)?
And what about saving the Product object?
How should i handle the brand and deliverytime data?
What is the beste practice or pattern to handle this kind of situations?
Sorry for this noobish question but i wasnt sure where to look for (tags to search for) :/
EDIT:
Ok lets say i dont want to use somekind of ORM framework (Doctrine, dORM, redbean etc) since it would be a gigantic overkill for my little system + i am realy want to know how to create the mapping myself for learning purposes... any suggestions?
This is a style I like to use
class ProductModel {
public function find($id) {
// fetch product with your database code using parameters to filter
$product = new ProductEntity(
// initialize with non foreighn values (eg. $row->id, $row->name)
new ProductBrandEntity($row->brand_id, $row->brand_name); // available because you joined brand??
new ProductDeliveryTime(/* initialize */)
);
return $product;
}
}
I like to call the object from the database Entities but you can call them whatever you want.. It's basically what you suggested in your question but i prefer to have a Model (from MVC) to initialize the entities and not the entities initializing themselves. You should do some research on ORMs and ActiveRecord because this work has basically already been done for you!