zf2 entity with joins - zend-framework2

I need to combine information from several tables.
In case of using entity I should create all possible fields as properties + setters/getters for them.
But on saving object - I should split / unset all properties which is not in main table.
Possibly there is more "true" way to deal with it, without using doctrine etc.

If I were you, I would take a closer look to Hydrators:
ZF2 docs on hydrators
Create a new class that implements the HydratorInterface:
namespace Zend\Stdlib\Hydrator;
interface HydratorInterface
{
/**
* Extract values from an object
*
* #param object $object
* #return array
*/
public function extract($object);
/**
* Hydrate $object with the provided $data.
*
* #param array $data
* #param object $object
* #return void
*/
public function hydrate(array $data, $object);
}
You need to implement 2 functions:
extract($object);
Extract will create an array from an object.
hydrate(array $data, $object);
Hydrate will create an object from an array.
When you do a select you can have all fields in 1 array, so there you have no problem to put it in the object. When extracting, you want it split up. You could implement extract in such a way:
public function extract($object)
{
return array(
'tbl1' => array(
'fld1' => $object->getFld1(),
'fld2' => $object->getFld2(),
'fld3' => $object->getFld3(),
),
'tbl2' => array(
'fld4' => $object->getFld4(),
'fld5' => $object->getFld5(),
),
'tbl3' => array(
'fld6' => $object->getFld6(),
'fld7' => $object->getFld7(),
'fld8' => $object->getFld8(),
'fld9' => $object->getFld9(),
),
);
}
When you then extract the data, you can pass each group of data to the correct table for insert or update.

Related

Order CollectionType in FormType with many-to-many relation

I have a relation many-to-many.
In Entity "Progetti" i have:
/**
*
* #var \Doctrine\Common\Collections\ArrayCollection $attivita
*
* #ORM\ManyToMany(targetEntity="Attivita", inversedBy="progetti",cascade={"persist", "remove" })
* #ORM\JoinTable(name="progetti_attivita",
* joinColumns={#ORM\JoinColumn(name="progetti_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="attivita_id", referencedColumnName="id")}
* )
*/
protected $attivita;
In Entity "Attività" i have:
/**
*
* #var \Doctrine\Common\Collections\ArrayCollection $progetti
* #ORM\ManyToMany(targetEntity="Progetti", mappedBy="attivita")
*/
protected $progetti;
Ok.
The JoinTable "progetti_attivita" has "attivita_id" and "progetti_id".
Now i added a new field to the JoinTable "progetti_attivita" and i called it "position". it's an integer.
I have the ProgettiType Form:
$builder
->add('nomeProgetto')
->add('descProgetto')
->add('noteProgetto')
->add('attivita', CollectionType::class, array(
'entry_type' => AttivitaType::class,
'allow_add' => true,
));
Ok.
I have the form that display all "attività" for "progetti".
My question is:
How can i say to Form to display "attivita" ordered by "position" ?
If you always want your collection to be ordered then you can add the OrderBy property to your Doctrine mapping:
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/ordered-associations.html
/**
* #ManyToMany(targetEntity="Attivita")
* #OrderBy({"position" = "ASC"})
**/
private $attivitas;
An alternative approach would be to add a getAttivitaOrdered method to your Progetti entity:
class Progetti
getAttivitaOrdered() // return ordered list
->add('attivitaOrdered', CollectionType::class ...
Not sure if you will need a setAttivitaOrdered method or not when you post.
And your question is actually a little bit confusing. Adding a property to your join table means that you need to use OneToMany and ManyToOne relations instead of ManyToMany. So a few more adjustments will be needed.

Validator\Db\RecordExists with multiple columns

ZF2 docs show the following example in terms of using Db\RecordExists validator with multiple columns.
$email = 'user#example.com';
$clause = $dbAdapter->quoteIdentifier('email') . ' = ' . $dbAdapter->quoteValue($email);
$validator = new Zend\Validator\Db\RecordExists(
array(
'table' => 'users',
'field' => 'username',
'adapter' => $dbAdapter,
'exclude' => $clause
)
);
if ($validator->isValid($username)) {
// username appears to be valid
} else {
// username is invalid; print the reason
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
I’ve tried this using my own Select object containing a more complex where condition. However, isValid() must be called with a value parameter.
In the example above $username is passed to isValid(). But there seems to be no according field definition.
I tried calling isValid() with an empty string, but this does not produce the desired result, since Zend\Validator\Db\AbstractDb::query() always adds the value to the statement:
$parameters = $statement->getParameterContainer();
$parameters['where1'] = $value;
If I remove the seconds line above, my validator produces the expected results.
Can someone elaborate on how to use RecordExists with the where conditions in my custom Select object? And only those?
The best way to do this is probably by making your own validator that extends one of Zend Framework's, because it doesn't seem like the (No)RecordExists classes were meant to handle multiple fields (I'd be happy to be proven wrong, because it'd be easier if they did).
Since, as you discovered, $parameters['where1'] is overridden with $value, you can deal with this by making sure $value represents what the value of the first where should be. In the case of using a custom $select, $value will replace the value in the first where clause.
Here's a hacky example of using RecordExists with a custom select and multiple where conditions:
$select = new Select();
$select->from('some_table')
->where->equalTo('first_field', 'value1') // this gets overridden
->and->equalTo('second_field', 'value2')
;
$validator = new RecordExists($select);
$validator->setAdapter($someAdapter);
// this overrides value1, but since isValid requires a string,
// the redundantly supplied value allows it to work as expected
$validator->isValid('value1');
The above produces the following query:
SELECT `some_table`.* FROM `some_table` WHERE `first_field` = 'value1' AND `second_field` = 'value2'
...which results in isValid returning true if there was a result.

yii CdbCriteria nested join

I have trouble creating a CdbCriteria with nested join. Here's the code in model (sorry it's in Indonesian. Hopefully anyone can understand the query):
public function report() {
$criteria=new CDbCriteria;
$criteria->alias = "p";
$criteria->select = "p.tanggal_transaksi,
MONTH( p.tanggal_transaksi ) AS bulan,
p.kode,
p.kode_supplier,
s.nama,
d.kode_bahan_baku,
b.nama_barang,
d.jumlah_kg,
d.jumlah_cones,
d.harga_satuan,
d.harga_satuan * d.jumlah_cones AS total,
FLOOR(d.harga_satuan * d.jumlah_cones * p.ppn / 100) AS ppn,
(d.harga_satuan * d.jumlah_cones) + FLOOR(d.harga_satuan * d.jumlah_cones * p.ppn / 100) AS total_akhir
";
$criteria->join = "JOIN m_supplier s ON s.kode = p.kode_supplier
RIGHT JOIN t_pembelian_detail d ON d.kode_pembelian = p.kode
JOIN m_bahan_baku b ON b.kode = d.kode_bahan_baku
";
$criteria->together = TRUE;
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
));
}
The name of the model is TPembelian and here is the relation:
public function relations()
{
return array(
'supplier' => array(self::BELONGS_TO, 'MSupplier', 'kode_supplier'),
'tPembelianDetails' => array(self::HAS_MANY, 'TPembelianDetail', 'kode_pembelian'),
);
}
In a controller, I wrote these lines of codes to just simply print out each attributes of CActiveDataProvider:
$model = new TPembelian('search');
$dataProvider = $model->report();
foreach ($dataProvider->getData() as $data) {
echo "<pre>".print_r($data->attributes, 1)."</pre>";
}
the problem is, it only prints out the attributes from TPembelian model (which use alias "p"). Why are other attributes (with other alias beside "p") not printed?
I have searched for a while and it appears that CActiveDataProvider not returning one long query, but instead it returns many query with HAS_MANY relation. Someone said to use "together" and set it to TRUE (I wrote it already in the code above) to make it returns one long query, but it's still not working. Can anybody please help me?
Note:
in the first code, m_supplier, t_pembelian_detail, and m_bahan_baku are tables, not models

ZF2 Db\RecordExists - Check additional columns

I have a problem with ZF2 RecordExists method. I will explain my problematic case/scenario.
Table: users
Columns: id, emailaddress, websitename
Sample Records:
1, user1#email.com, 1site.com
2, user2#email.com, 1site.com
3, user3#email.com, 2site.com
4, user4#email.com, 2site.com
5, user5#email.com, 1site.com
6, user6#email.com, 3site.com
7, user7#email.com, 4site.com
I am using the following snippet for already exist condition.
//Check that the email address exists in the database
$validator = new Zend\Validator\Db\RecordExists(
array(
'table' => 'users',
'field' => 'emailaddress'
)
);
if ($validator->isValid($emailaddress)) {
// email address appears to be valid
} else {
// email address is invalid; print the reasons
foreach ($validator->getMessages() as $message) {
echo "$message\n";
}
}
As per the above snippets, user1#email.com cannot register again. Because, that emailaddress is exist in table.
But, i would like to do register with 2site.com. Because, user1#email.com is in 1site.com.
So, user1#email.com cannot register with 1site.com again. But, user1#email.com can register with 2site.com.
How is it possible? Let me know your suggestions.
There are two way to do this.
First using Excluding Record methods
Where you exclude the record of websitename field value.
Zend\Validator\Db\RecordExists and Zend\Validator\Db\NoRecordExists
also provide a means to test the database, excluding a part of the
table, either by providing a where clause as a string, or an array
with the keys “field” and “value”.
$email = 'user#example.com';
$clause = $db->quoteInto('email = ?', $email);
$validator = new Zend\Validator\Db\RecordExists(
array(
'table' => 'users',
'field' => 'username',
'exclude' => $clause
)
);
if ($validator->isValid($username)) {
// username appears to be valid
} else {
// username is invalid; print the reason
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
Second by writing your own custom validtor.
You need to extend AbstractDB class and create your own class on directions of RecordExists Class. In your own cust class your can define your own query and pass it to isValid function.
I have created a Custom Validator oposite to Exclude, which is include.
include is reserve word, Now sure if it will work.
check it here
More readings on this
Guidlines 1
Please have look to existing validtor for creating your own custom validatorCustom validator guildline
Chain validator 2

magento using join in grid.php prepareCollection

Can someone tell me how to make a join within magento
Here is the problem:
<?//kleurtjes
$collection= Mage::getModel('faq/faq')->getCollection();
$collection->getSelect()->join(array('faqcat' => $this->getTable('faqcat/faqcat')), 'faqcat.faqcat_id=faq.faqcat_id' , array('faqcat.*'));
?>
i am trying to make a join with the table faqcat where i use the key faqcat_id .
futher i want that faqcat.name + faq.faq_id are being selected cos these are the values i want to use in colums.
<?
protected function _prepareColumns()
{
$this->addColumn('faq_id', array(
'header' => Mage::helper('faq')->__('ID'),
'align' =>'right',
'width' => '50px',
'index' => 'faq_id',
));
$this->addColumn('name', array(
'header' => Mage::helper('faqcat')->__('Titel'),
'align' =>'left',
'index' => 'name',
));
}
?>
after trying 1000 combinations i dont know what to do anymore ... who is willing to help me
this is the complete function:
<?
protected function _prepareCollection()
{
$collection= Mage::getModel('faq/faq')->getCollection();
//$collection->getSelect()->join(array('faqcat' => $this->getTable('faqcat/faqcat')), 'faqcat.faqcat_id=faq.faqcat_id' , array('faqcat.*'));
$id = Mage::getModel('customer/session')->getCustomer()->getId();
$this->setCollection($collection);
// }
return parent::_prepareCollection();
}
?>
just to be clear this is the sql i want to have , but then the magento way
<?//kleurtjes
SELECT faq.faq_id as id, faqcat_name as name
FROM faq
JOIN faqcat
USING ('faqcat_id')
?>
Try this:
$collection->getSelect()
->join($this->getTable('faqcat/faqcat'), "faqcat.faqcat_id = main_table.faqcat_id", array(faqcat.*));
You can see the sql that will actually be run to fetch the collection by:
Mage::log($collection->getSelect()->__toString());
The Varien_Db_Select class is based on Zend_Db_Select, so the Zend documentation is a good reference.
i have just started developing magento extension (loving it) and this is the 2nd part in it where i have to show a grid from two tables. (whew).
but its not that easy after surfing internet a lot i achieved mine result by doing this.
$collection = Mage::getModel('linkdirectory/linkdirectory')->getCollection();
$resource = Mage::getSingleton('core/resource');
$collection->getSelect()
->join(
array('lk'=>$resource->getTableName('linkdirectory/linkcategory')),
'lk.cat_id = main_table.cat_id',
array('lk.cat_title','lk.cat_id')
);
/* mine was showing this */
/SELECT main_table., lk.cat_title, lk.cat_id FROM linkdirectory AS main_table INNER JOIN linkcategory AS lk ON lk.cat_id = main_table.cat_id*/
/* to print the current query */
$collection->printlogquery(true);exit;

Resources