How to create a dynamic form helper based on database tables? - cakephp

Using cakephp 2.3.0
The conditions for the helper are:
1) I need to use many drop down boxes in multiple forms and I don't want to do loadModel in each and every controllers(12)
2) Need to fetch the datas from database tables
3) I am thinking of not baking files for the tables.
4) Want to use custom mysql queries.
E.g I've a table named countries and the dropdown should be key=>abbreviated name and name=>full name.
Any suggestions will be appreciated..

I created a function in helper
`
public function getDropDownList($table,$key,$value)
{
//$db =& ConnectionManager::getDataSource('default');
$fields = $key.','.$value;
$CommercialHeaders =& ClassRegistry::init('tablename');
$sql = $CommercialHeaders->query('select '.$fields.' from '.$table.' as DropDown');
foreach($sql as $val)
{
$countryList[$val['DropDown'][$key]] = $val['DropDown'][$value];
}
//debug($countryList);
return $countryList;
}
`
and just called it from the view by passing table name and field, which will generate an array so it solves my problem without setting it in appcontroller or using requestAction

Related

Cakephp 3 - How to integrate external sources in table?

I working on an application that has its own database and gets user information from another serivce (an LDAP is this case, through an API package).
Say I have a tables called Articles, with a column user_id. There is no Users table, instead a user or set of users is retrieved through the external API:
$user = LDAPConnector::getUser($user_id);
$users = LDAPConnector::getUsers([1, 2, 5, 6]);
Of course I want retrieving data from inside a controller to be as simple as possible, ideally still with something like:
$articles = $this->Articles->find()->contain('Users');
foreach ($articles as $article) {
echo $article->user->getFullname();
}
I'm not sure how to approach this.
Where should I place the code in the table object to allow integration with the external API?
And as a bonus question: How to minimise the number of LDAP queries when filling the Entities?
i.e. it seems to be a lot faster by first retrieving the relevant users with a single ->getUsers() and placing them later, even though iterating over the articles and using multiple ->getUser() might be simpler.
The most simple solution would be to use a result formatter to fetch and inject the external data.
The more sophisticated solution would a custom association, and a custom association loader, but given how database-centric associations are, you'd probably also have to come up with a table and possibly a query implementation that handles your LDAP datasource. While it would be rather simple to move this into a custom association, containing the association will look up a matching table, cause the schema to be inspected, etc.
So I'll stick with providing an example for the first option. A result formatter would be pretty simple, something like this:
$this->Articles
->find()
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$userIds = array_unique($results->extract('user_id')->toArray());
$users = LDAPConnector::getUsers($userIds);
$usersMap = collection($users)->indexBy('id')->toArray();
return $results
->map(function ($article) use ($usersMap) {
if (isset($usersMap[$article['user_id']])) {
$article['user'] = $usersMap[$article['user_id']];
}
return $article;
});
});
The example makes the assumption that the data returned from LDAPConnector::getUsers() is a collection of associative arrays, with an id key that matches the user id. You'd have to adapt this accordingly, depending on what exactly LDAPConnector::getUsers() returns.
That aside, the example should be rather self-explanatory, first obtain a unique list of users IDs found in the queried articles, obtain the LDAP users using those IDs, then inject the users into the articles.
If you wanted to have entities in your results, then create entities from the user data, for example like this:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
For better reusability, put the formatter in a custom finder. In your ArticlesTable class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
Then you can just do $this->Articles->find('withUsers'), just as simple as containing.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods

CakePHP 3: get a list of all table objects

I'm looking for a way to get a list of all available table objects. These are all the classes that are (by default) located under App/Modal/Table and that are handled by TableRegistry. How to get a list of all those objects?
I know it's possible to fetch all tables of the db:
$tables = ConnectionManager::get('default')->schemaCollection()->listTables();
And then using TableRegistry::get() to get the table object.
But this is not possible for my solution, because there are two cases where this does not work:
custom table names that are different to the table object name
plugin table objects
Any ideas?
Edit: Why? I need all table objects that use a behavior X. In my case a custom SearchableBehavior, which updates a searchindex table on each afterSave event for the saved entity. To update the searchindex for all entities of all tables, I need to know which tables are using the SearchableBehavior and call their update method manually.
$tables = glob(APP."Model".DS."Table".DS."*Table.php");
$tablesNames = [];
foreach ($tables as $name){
$item = explode('Table.php', basename($name));
$tablesNames[] = $item[0];
}
pr(tablesNames);
Write an event listener that listens on Model.initialize and then do a check on the subject, which is the table object if the table has your behavior. Then do something with that list.
If this doesn't work for you - you give zero background info - iterate over the apps Model/Table folder and plugin folder and the vendor folders and search for Model folders and check for *Table.php files. Then try to instantiate the table objects based on the path / namespace and filename and check the models. But this is not very fast, you should cache the resulting list.
I recently had a similar use case, where I needed to access all Table Objects, to initialize the data in the database once, in a console command.
I did it by first building an array of all the paths where the Table Object Classes could reside, then iterating over all files and using the ones ending in "Table.php". Note that this approach might need to be modified slightly depending on your use case.
<?php
use Cake\Core\Plugin;
use Cake\ORM\TableRegistry;
use Cake\Filesystem\Folder;
// Building an array of all possible paths. Firstly the src directory:
$tableClassPaths = [
APP . 'Model' . DS . 'Table' . DS,
];
// Secondly, all loaded plugins:
foreach(Plugin::loaded() as $plugin) {
$dir = Plugin::classPath($plugin) . 'Model' . DS . 'Table' . DS;
if(is_dir($dir)) {
$tableClassPaths[$plugin] = $dir;
}
}
// Iterating over each file in each folder.
$tableObjects = [];
foreach($tableClassPaths as $plugin => $dir) {
foreach(new \FilesystemIterator($dir) as $file) {
// All Files ending in Table.php might be relevant
if($file instanceof \SplFileInfo && $file->isFile()
&& mb_substr($file->getFilename(), -9) === 'Table.php'
) {
$className = mb_substr($file->getFilename(), 0, -9);
if(is_string($plugin)) {
$className = $plugin . '.' . $className;
}
$tableObject = TableRegistry::getTableLocator()->get($className);
// Here you can use / filter the Tables, for example by checking for the presence of a behavior "Indexable":
if($tableObject->hasBehavior('Indexable')) {
$tableObjects[] = $tableObject;
}
}
}
}
?>
Keep in mind, that this is only really suitable for very narrow circumstances, since this completely sidesteps the regular MVC patterns of CakePHP.

how to insert data in sales_flat_quote_item_option table of magento?

I am working on magento 1.7 . I need to insert data in sales_flat_quote_item_option table.I tried with following code:-
$customerId= Mage::helper('customer')->getCustomer()->getId();
$product_id = $_REQUEST['id1'];
$model = Mage::getModel('catalog/product');
$_product = $model->load($product_id);
$quoteObj=Mage::getModel('sales/quote')->assignCustomer($customerId);
$option = array('options'=>array(
"option_id1" => 'option_value1',
"option_id2" => 'option_value2'
));
$request = new Varien_Object();
$request->setData($option);
$quoteObj->addProduct($productObj,$request);
but not able to insert data in this table. Actually this table contains custom options value.I I don't have custom options so just insert another values in this table which is coming from another form.Can anybody help me?
I'm not 100% sure what you are trying to do, but I think it would be better if
Use an observer
Add your options to 'additional_options' instead of 'options'
See Magento - Quote/order product item attribute based on user input

how to display the data returned by get_by_user_id() in DataMapper Code Igniter?

I am new to code igniter data mapper. I have a table called user, and I am trying to retrieve data from the database table and show them to the user.
Here is what I have in the model:
$u=new User();
$results=$u->get_by_user_id($id);
//$results here will be set to huge bunch of none sense data( which also includes the row that I am looking for as well)
if ($u->exists())
{
foreach ($results->all as $row){
$data['user']['first_name']=($row->user_first); //this where I am stuck ..
$data['user']['last_name']=($row->user_last);//this is also where I am stuck..
}
I don't know how to treat results to get a required fields I am looking for and store them in the $data I am passing to the user to view.
Thanks!
When you call get_by_x() on the model, the fields will be populated with data and you can access them like this:
$u = new User();
$u->get_by_user_id($id);
if($u->exists())
{
// you can access the table columns as object fields
$data['user']['first'] = $u->first;
$data['user']['last'] = $u->last;
}
else
{
$data['error'] = 'No such user!';
}
Have a look at the documentation which is really helpful: see Get and Get By.
Also, DataMapper expects all tables to have an id column: see Table Naming Rules. If your column is named id you should then call $u->get_by_id($id) instead of $u->get_by_user_id($id).

Using existing field name as different name

i have existing website.
and i write the new back-end (in cakephp) without changing front-end programm
the discomfort that
db table has field names as
id
news_date
news_title
news_content
is it possiable to do something in cakephp model file (reindentify the field names)
so i can use model in controller as
News.date
News.title
News.content
What you need to do is setup some very basic virtual fields in your news model. Something like this should suit your needs.
public $virtualFields = array(
'title' => 'news_title',
'date' => 'news_date',
'content' => 'news_content'
);
Also do yourself a favour by checking out the other model attributes that could help you out, you'll want to set displayType as new_title I'd imagine.
Is said by Dunhamzz, virtualFields are a good solution until you want to work with these new field-names.
Since I assume your frontend needs to use the old names from the database I would go with the afterFind-callback in your model.
Let's say you've got the model news.php:
# /app/model/news.php
function afterFind($results) {
foreach ($results as $key => $val) {
if (isset($val['News']['title'])) {
$results[$key]['News']['news_title'] = $val['News']['title']);
# unset($results[$key]['News']['title']); //use this if you don't want the "new" fields in your array
}
if (isset($val['News']['date'])) {
$results[$key]['News']['news_date'] = $val['News']['date']);
# unset($results[$key]['News']['date']); //use this if you don't want the "new" fields in your array
}
if (isset($val['News']['content'])) {
$results[$key]['News']['news_content'] = $val['News']['content']);
# unset($results[$key]['News']['content']); //use this if you don't want the "new" fields in your array
}
}
return $results;
}
You need to rename the database-fields to your new wanted value. You then can use these within conditions like every other field.
Only difference is, that you get back an array where all your fields have been renamed to your frontend-fields.
For more information about the available callback-methods have a look here: Callback Methods

Resources