Example:
class Products extends Zend_Db_Table_Abstract
{
protected $_name = 'products';
protected $_referenceMap = array(
'Bug' => array(
'columns' => array('bug_id'),
'refTableClass' => 'Bugs',
'refColumns' => array('bug_id')
)
);
}
$object = new Products();
$select = $object->select()->from()->Join('Bug');
Instead of defining the full join statement
As far as I can tell $_referenceMap is not used in that way. $_referenceMap defines the relationship that a table row has with other tables.
That's why the associated findDependentRowset(), findManyToManyRowset() and findParentRow() are found in Zend_db_Table_Row_Abstract. These methods create the Joins.
So to get the dependent rows from Bugs, using a select object, you would do something like this, assuming that Products has a one-to-many relationship with Bugs;
class Products extends Zend_Db_Table_Abstract
{
protected $_name = 'products';
protected $_dependentTables = array('Bugs');
}
class Bugs extends Zend_Db_Table_Abstract
{
protected $_referenceMap = array(
'Products' => array(
'columns' => array('bug_id')
,'refTableClass' => 'Products'
,'refColumns' => array('bug_id')
)
);
}
To get dependent rows you first have to fetch a parent row.
$products = new Products();
$productRow = $products->find(123)
->current();
You can refine the join by using Zend_Db_Select
$select = $products->select()
->where('foo_bar = ?', 'cheese')
->limit(2);
Finally querying the dependent rows by passing in the select object instead instead of a rule key.
$bugRowset = $productRow->findDependentRowset('Bugs', 'Products', $select);
I think this will work, I'll have to check tomorrow morning.
This is usefull for one row, but not for the whole table (or several rows). I usually need queries affecting more than one row... Zend should implement the option mentioned by Phliplip, or something similar:
$select = $object->select()->from()->Join('Bug');
Note: I mean, using only one query
Related
I'm trying to handle how a field within a CrudController stores or updates the data on the particular model in a completely custom way. I would like the traitStore() and traitUpdate() methods to ignore this field entirely, but would like the data to still be passed in via the request. This is specifically in reference to a many-many relationship using a select2_multiple field.
I would like it so that the relationship ID's are passed via the request object to the Store or Update methods, but I DO NOT want the traitStore() or traitUpdate() methods to actually perform updates on that particular field reference.
For example...
I have this field within my crud controller
$this->crud->addField(
[
'label' => "Groups",
'type' => 'select2_multiple',
'name' => 'groups',
'entity' => 'groups',
'attribute' => 'title',
'model' => "App\Models\Group",
'pivot' => true
]
);
And I'm overriding the Store and Update Methods like so.
public function store()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitStore();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
public function update()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitUpdate();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
Looking at my comments I would like to get a reference to the groups and handle updating the model however I want.
I've tried to unset the groups value in the request, unset($this->request{'groups'}), but it still updates / removes the relationships when I do that.
Here is what you need to do to remove the references from being updated by the CrudController.
public function update()
{
$this->crud->setValidation(UserRequest::class);
$request = clone $this->request;
$this->crud->request->request->remove('groups');
$this->crud->removeField('groups');
$groups = $request->groups
$response = $this->traitUpdate();
return $response;
}
I found an easy way to ignore/pass form field.
Example:
In your form fields have first_name, last_name, gender and in your database is only have fullname, gender then you wanna create/update the form, it will show Column not found: 'first_name' not found...,
How to fix it:
Add $fillable in the model and fill the array data with name field that you want to store/update. In example case $fillable = ['fullname', 'gender'];
Then, just use mutators inside the model too.
public function setFullnameAttribute(){
return $this->attributes['fullname'] = \Request::input('first_name') . ' ' . \Request::input('last_name');
}
NB: You should have hidden field name 'fullname' in your CrudController.
$this->crud->addField(['name' => 'fullname', 'type' => 'hidden']);
I am learning how to do simple queries using the Yii2 framework. I use PostgreSQL.
I am trying to join two tables and get the data from both tables with a where condition.
The tables are called Admins and Persons.
The join use field called idadm.
The condition is idadm = 33. This works great but the result has data only from the Admins table and I need data from the other table.
Here is my example:
$query = \app\models\Admins::find()
->select('*')
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
I am following the Yii2 official guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
Update: Here I show the updated code that doesn't solve de problem:
You need to write all column name in select().
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
And also you need to define person table attributes in Admin model.
Second way is get records as array,so you dont need to define attributes in Admin model.
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->asArray()
->all();
Ensure that active record has required relations, e.g. something like follows:
class Admins extends \yii\db\ActiveRecord {
public function table() {
return "admins";
}
public function getPersons()
{
return $this->hasMany(Person::className(), ['idadm' => 'idadm']);
}
}
class Person extends \yii\db\ActiveRecord {
public function table() {
return "persons";
}
}
Then use joinWith to build query:
$query = Admins::find()
->joinWith('persons')
->limit(1);
$result = $query->createCommand()->getSql();
echo $result;
Here is produced query:
SELECT `admins`.* FROM `admins`
LEFT JOIN `person` ON `admins`.`idadm` = `person`.`idadm` LIMIT 1
Taking the below as an example, OrderType is based on the entity Order. The form that is required needs to contain the following two EntityType dropdowns within it:
Category (this does not exist in Order - it is just to subset the dropdown of Product to make it more manageable)
Product
The identifying Category variables (Category_id and CatName) only exists within the Product entity (the Order can include multiple Products) and as a result, Symfony throws back an error saying:
Neither the property "category_id" nor one of the methods "getcategory_id()", "category_id()", "iscategory_id()", "hascategory_id()", "__get()" exist and have public access in class "AppBundle\Entity\Order".
Is there a way that this Category field can be included even though it doesn't exist in the Order entity?
It doesn't seem right to add category_id to the Order entity. I was thinking of something along the lines of below using 'mapped'=>'false' but I can't get it to work:
$builder
->add('category_id','entity',array(
'class'=>'AppBundle:Product',
'placeholder' => '-- Choose --',
'choice_label'=>'CatName',
'mapped'=>'false',
'query_builder'=>function(EntityRepository $er) {
return $er->createQueryBuilder('p');
}))
...and then after an Ajax response, feed in the category back in with $category?
->add('products','entity',array(
'class'=>'AppBundle:Order',
'placeholder' => '-- Choose --',
'choice_label'=>'ProductName',
'query_builder'=>function(EntityRepository $er, $category ) {
return $er->createQueryBuilder('p')
->where('p.category_id = :id')
->setParameter('id', $category )
->orderBy('p.ProductName','ASC');
}));
}
As you say, adding a Category property to the Order entity just for forms is less than ideal. What I would do is make a OrderCategoryType and pass in Order and Category as an array.
// Controller
$order = new Order();
$category = new Category(); // Or create from $order->getProduct()
$data = ['order' => $order, 'category' => $category);
$form = $this->createFormBuilder($data)
->add('order',new OrderType(),
->add('category',new CategoryType()
);
You will have to do some messing around to keep everything in sync but it should work just fine.
I'm developing an application in Zend Framework to handle the rentals for a commercial property rental company. The company has multiple buildings which each have multiple floors, which each have multiple units.
The models I've setup just extend Zend_Db_Table_Abstract, and I've set them up with $_dependentTables and $_referenceMaps with cascading delete, such that when I delete a floor, all the units within it are deleted too, and when I delete a building, all the floors in it are deleted. However, when I delete a building and the floors are deleted, the delete is not cascaded through to each floor's units. (edit: I'm using MySQL, so I am not able to use referencial integrity at the db level.)
I've looked at how the deletes are cascaded, and it appears they aren't cascading because the cacaded deletes are executed using a Zend_Db_Table object, not a Zend_Db_Table_Row object (which you have to use to achieve cascading).
Is there any way I can update the system so that the delete cascades all the way down? Is there a way I can modify the relationships of my classes, or would I need to use something like Doctrine?
(I guess I could override the delete() method for the row of each table or something, but I just wondered if this is possible using the relationships functionality of ZF?)
If it helps, here's the relevant parts of the class definitions:
class Buildings extends Zend_Db_Table
{
protected $_dependentTables = array('Floors');
}
class Floors extends Zend_Db_Table
{
protected $_dependentTables = array('Units');
protected $_referenceMap = array(
'Building' => array(
'columns' => 'building_id',
'refTableClass' => 'Buildings',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
));
}
class Units extends Zend_Db_Table
{
protected $_referenceMap = array(
'Floor' => array(
'columns' => 'floor_id',
'refTableClass' => 'Floors',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
));
}
Just to be sure... Are you using a RDBMS that doesn't support referencial integrity?
For my taste, it's easier (and more portable, in case you decide to access the DB from another application in the future) to declare the ON DELETE CASCADE in your RDBMS (provided that it allows it), instead of emulating it with the framework.
It seems that the Zend Framework documentation also advices in this sense:
http://framework.zend.com/manual/en/zend.db.table.relationships.html#zend.db.table.relationships.cascading
Suppose that i have a table 't' with fields 'id' int 11, 'name' varchar 50, and 'description' text. if the text is very large, and i am querying the database with 'description' and without 'description', will there be any difference?
and how to select only 'id' and 'name' in the Model that extends Zend_Db_Table_Abstract class?
In term of perfomances I can't really help, but it should not make a big difference.
For your question about how to select fields in a query look at the following
class Model_DbTable_T extends Zend_Db_Table_Abstract{
protected $_name = 't'; // this line is optionnal if your table has the same name as the class
public function fetchSomeFields(){
$query = $this->select()
->from($this,
array('id')); // you could also include the 'as' statement in the field name to make it look like 'id as otherName'
return $this->fetchAll($query);
}
}