I'm trying to achieve a cascading UPDATE and DELETE effect in a MyISAM database (similar effect as you can create in InnoDB tables with foreign keys). Two example tables:
albums
photos (has an album_id column that is a "foreign key" to the albums table)
Now when I delete a row in the albums table I would like Zend_Db_Table to automatically delete all related rows in the photos table. This is what I have in the albums table:
protected $_name = 'albums';
protected $_dependentTables = array(
'Photos'
);
And I have this in the photos table:
protected $_name = 'photos';
protected $_referenceMap = array(
'Album' => array(
'columns' => array('album_id'),
'refTableClass' => 'Albums',
'refColumns' => array('id')
)
);
Yes when I delete a row in the albums table, the photos from that album do not get removed.
This is how I'm removing the album:
public function remove($id)
{
$where = $this->getAdapter()->quoteInto('id = ?', $id, 'INTEGER');
return $this->delete($where);
}
You need to setup the cascade delete. So your reference map should be:
protected $_referenceMap = array(
'Album' => array(
'columns' => array('album_id'),
'refTableClass' => 'Albums',
'refColumns' => array('id'),
'onDelete' => self::CASCADE
));
See full description of cascading operations here: http://framework.zend.com/manual/en/zend.db.table.relationships.html#zend.db.table.relationships.cascading
NB Cascading operations are only triggered when functions called on the actual row of the results set (i.e. Zend_Db_Table_Row class). To trigger the delete function in this example:
$album = $albums->find($id);
$album->delete();//This triggers the cascading delete
Related
My question is about creating collection of entities. I know about "How To Embed Collection Forms" and successfully used it. But in this case I have:
Simple class
class Thing
{
/**
* #ORM\ManyToMany(targetEntity="DicStyle", mappedBy="things")
* ....
*/
protected $styles;
public function __construct()
{
$this->styles = new ArrayCollection();
}
}
Dictionary of styles
class DicStyle
{
.....
}
I don't need to create form for DicStyle objects, because this is read only objects = dictionary (unchangeable). So, I want to create a form with something like this:
$builder->add('styles', 'collection', array(
'type' => 'entity', 'options' => array(
'class' => 'MyEntityBundle:DicStyle'
)
))
Of course it's pseudo-code. I can not imagine how to implement it.
The result
Suppose, I have:
Table "Thing" with one row (id = 1).
Table "DicStyle" with 6 rows (id = from 1 to 6).
Table "mtm_thing_dicstyle" (many-to-many table)
In the form, I choose two DicStyle (id=3, id=5) for the Thing. So, the mtm_thing_dicstyle contains:
thing_id dicstyle_id
-------- ------------
1 3
1 5
Try this form:
$builder->add('styles', 'entity', array(
'class' => 'MyEntityBundle:DicStyle'
'property'=>'name' //what property do you want to see when you select,
'multiple" => true //you'll be able to select many DicStyle
'expanded' => false //it'll shown on a multiple choice select tag
)
);
To select one or more objects in a form to relate them to your result you can use the form field type "entity". (See form-types)
$builder->add('styles', 'entity', array(
'class' => 'MyEntityBundle:DicStyle',
'property' => 'name', // property you want to be displayed
'expanded' => true,
'multiple' => true
)
);
This would render checkboxes and therefore the possibility to select multiple entities to be referenced.
Keep in mind, if you use multiple => true there are two options:
expanded => false: renders a multiselect field
expanded => true: renders checkboxes
See form-entity-type-options
What is right naming or what am I missing to get automagic run for two word named Model. Actual model belong to the two words named model.
Exact example:
Tour belongs to Accommodation type.
in database there is table tours and table accommodation_types
foreign key from tours is tours.accommodation_type_id
Snapshots of code below.
ToursController.php
public function add() {
//...
$accommodation_types = $this->Tour->AccommodationType->find('list');
//...
$this->set(compact('accommodation_types', ...));
}
Tour.php
//...
public $belongsTo = array(
//...
'AccommodationType' => array(
'className' => 'AccommodationType',
'foreignKey' => 'accommodation_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//...
);
Tours/add.ctp (inside a form)
echo $this->Form->input('accommodation_type_id', array('label' => 'Accommodation type'));
As per convention the view vars names should be camelBacked. So rename the view var from $accommodation_types to $accommodationTypes. If you don't follow convention you have to explicitly specify the options var to use like this:
echo $this->Form->input('accommodation_type_id', array('options' => $accommodation_types, 'label' => 'Accommodation type'));
I was trying an example of one to many relationship in zend framework, but I can't get results.
Here are the tables:
UsersTable:
<?php
class Application_Model_DbTable_UsersTable extends Zend_Db_Table_Abstract
{
protected $_name = 'users';
protected $_dependentTables = array('Application_Model_DbTable_BugsTable');
}
BugsTable:
class Application_Model_DbTable_BugsTable extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs';
protected $_dependentTables = array('Application_Model_DbTable_BugsProductsTable');
protected $_referenceMap = array(
'Reporter' => array(
'columns' => 'reported_by',
'refTableClass' => 'Application_Model_DbTable_UsersTable',
'refColumns' => 'username'
),
'Engineer' => array(
'columns' => 'assigned_to',
'refTableClass' => 'Application_Model_DbTable_UsersTable',
'refColumns' => 'username'
),
'Verifier' => array(
'columns' => array('verified_by'),
'refTableClass' => 'Application_Model_DbTable_UsersTable',
'refColumns' => array('username')
)
);
}
As you can see, this is one to many relationship from db table 'users' to table 'bugs' , where we have three foreign reference keys in bugs talbe.
Now when I try to use Zend methods for one to many relationships I always get empty results:
$tableUser = new Application_Model_DbTable_UsersTable();
$tableBugs = new Application_Model_DbTable_BugsTable();
$result= $tableUser->find(1);
$user= $result->current();
$userBugs = $user->findDependentRowset('Application_Model_DbTable_BugsTable','Verifier');
echo count($userBugs); //returns 0
$bugresult = $tableBugs->find(1);
$thisbug= $bugresult->current();
$verifier= $thisbug->findParentRow('Application_Model_DbTable_UsersTable','Verifier');
return $verifier //returns nothing
The proper data is in the database, when I for example do a query:
select * from bugs b, users u where b.reported_by=u.id and u.id=1;
I get the expected results. But when I try in zend no results.
Do you have any suggestions? Thanks.
In your sql u get bugs by field
reported_id
but in this code:
$result= $tableUser->find(1);
$user= $result->current();
$userBugs = $user->findDependentRowset('Application_Model_DbTable_BugsTable','Verifier');
echo count($userBugs); //returns 0
$bugresult = $tableBugs->find(1);
$thisbug= $bugresult->current();
$verifier= $thisbug->findParentRow('Application_Model_DbTable_UsersTable','Verifier');
return $verifier //returns nothing
u try to get bug by
verified_by
field
try this:
$result= $tableUser->find(1);
$user= $result->current();
$userBugs = $user->findDependentRowset('Application_Model_DbTable_BugsTable','Reporter');
echo count($userBugs);
I have 2 tables user and user_comment where user has many user_comments, i laid down the mapping being
User
$_dependentTables = array('User_Comments);
and
User_Comments
$_referenceMap = array(
'User' => array(
'columns' => 'id',
'refTableClass' => 'User',
'refColumns' => 'id'
)
);
Is there a way for me to do user->fetchAll() and get the user_comments without doing loop query (in cakephp it will do one query on user_comments where in (ids) then format it back to an array but i cant use cake). Is this possible in zend with me doing it manually? Thanks
Try this one
$sql=$this->getAdapter()->select()
->from("user_comment")
->join("user", "user.id=user_comment.userid")
->where("user_comment.id=?",$userId);
$result=$this->getAdapter()->query($sql)->fetchAll();
This might help u....
This is my first time using Zend Framework for an application and i don't know if I completely have by head around Models.
I have four tables: shopping_cart, product, product_unit, distributor.
shopping cart has an cart_id, product_id, unit_id and dist_id (shopping cart joins on the other tables with their corresponding id).
Before Zend I would create a class like this:
class ShoppingCart
{
function getItems()
{
$sql ="select * from shopping_cart, product, product_unit, distributor
where
shopping_cart.product_id = product.id AND
shopping_cart.unit_id = product_unit.id AND
shopping_cart.dist_id = distributor.id AND
cart_id = xxx";
$items = $this->db->getAll($sql);
}
One query to get all the information from the joined tables.
When I set up the relationship mapping in Zend_Db_Table_Abstract:
My Shopping Cart Model:
class Application_Model_ShoppingCart
{
function __construct()
{
$this->ShoppingCartTable = new Application_Model_DbTable_ShoppingCart();
}
function getItems()
{
$cart_items = $this->ShoppingCartTable->getItems($this->GetCartId());
return $cart_items;
}
}
class Application_Model_DbTable_ShoppingCart extends Zend_Db_Table_Abstract
{
protected $_name = 'shopping_cart';
protected $_rowClass = 'Application_Model_DbTable_ShoppingCart_Item';
protected $_referenceMap = array(
'Product' => array(
'columns' => 'product_id',
'refTableClass' => 'Application_Model_DbTable_Product',
'refColumns' => 'id'
),
'Distributor' => array(
'columns' => 'dist_id',
'refTableClass' => 'Application_Model_DbTable_Distributor',
'refColumns' => 'id'
),
'Unit' => array(
'columns' => 'unit_id',
'refTableClass' => 'Application_Model_DbTable_ProductUnit',
'refColumns' => 'id'
)
);
public function getItems($cart_id)
{
$where = $this->getAdapter()->quoteInto('cart_id = ?', $cart_id);
return $this->fetchAll($where);
}
}
In my controller:
$this->_shoppingCartModel = new Application_Model_ShoppingCart();
$items = $this->_shoppingCartModel->getItems();
IN my view :
foreach($this->items AS $item)
{
$item_product = $item->findParentRow('Application_Model_DbTable_Product');
$item_dist = $item->findParentRow('Application_Model_DbTable_Distributor');
$item_unit = $item->findParentRow('Application_Model_DbTable_ProductUnit');
}
when I have ten items in my cart the db profiler shows over sixty queries (WHOA) to view the cart items ( information across all four tables are displayed - product name, unit description, distributor name).
For each item it queries the shopping_cart, then querys the product table, then the product unit, then the distributor_table.
Is there a way to have this run as one query joining all the tables via Zend_Db_Table_Abstract relationships?
Will I have to go back to using db adapter in the my Application_Model_ShoppingCart class?
I want to abstract all data access to the table models (Application_Model_DbTable_ShoppingCart) and not have the Application_Model_ShoppingCart tied to a db handler.
Thanks in advance for advice, I love the Zend Framework but models are still hard for me to understand given the different conflicting ways people talk about using them.
In short, no, unfortunately it's not possible to get a set of table rows together with their relationships in a single query.
It's just that all methods dealing with relationships are defined for a row, not for a table.
But at least, you can form your sql with Zend_Db_Table_Select instead of writing it all manually.
Upd: Your code for fetching ShoppingCarts, in my opinion, should belong to the table (DbTable_ShoppingCart). So, the code you provided in the beginning could be transformed to the following:
class Application_Model_DbTable_ShoppingCart extends Zend_Db_Table_Abstract {
public function getItem($cart_id) {
$select = $this->select()
->from( array('sc' => 'shopping_cart'), array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('p' => 'product'), 'sp.product_id = p.id', array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('pu' => 'product_unit'), 'sp.unit_id = pu.id', array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('d' => 'distributor'), 'sp.dist_id = d.id', array(Zend_Db_Select::SQL_WILDCARD) )
->where('sp.cart_id = ?', $cart_id)
->setIntegrityCheck(false);
return $this->fetchAll($select);
}
}