I'm trying to get a complete list of all models under app/Model.
Already tried App::objects('Model') but it only retrieves loaded models.
Is this possible in CakePHP 2?
After some research I found that App::objects('Model') returns all models under app/Models but it doesn't include Plugin models.
To include all models (app models and plugin models) I created the next function:
/**
* Get models list
*
* #return array
*/
public static function getModels()
{
// Get app models
$models = App::objects('Model');
$models = array_combine($models, $models);
// Get plugins models
$pluginsFolder = new Folder(APP . 'Plugin');
$plugins = $pluginsFolder->read();
foreach ( $plugins[0] as $plugin ) {
$pluginModels = App::objects($plugin . '.Model');
foreach ($pluginModels as $pluginModel) {
$models[$plugin . '.' . $pluginModel] = $plugin . '.' . $pluginModel;
}
}
// Exclude tableless and application models
$dataSource = ConnectionManager::getDataSource('default');
$sources = $dataSource->listSources();
foreach($models as $key => $model) {
$table = Inflector::tableize(self::modelName($key));
if (stripos($model, 'AppModel') > -1 || !in_array($table, $sources)) {
unset($models[$key]);
}
}
return $models;
}
Maybe late but here is my version:
(loops through plugin models, and retrieves the table associated by opening the file, and searching for the variable $useTable)
/**
* Get models list.
* Retrieved from: https://stackoverflow.com/questions/38622473/get-models-list-in-cakephp-2
* #return array
*/
public static function getModels() {
// Get app models
$models = App::objects('Model');
$models = array_combine($models, $models);
// Get plugins models
$pluginsFolder = new Folder(APP . 'Plugin');
$plugins = $pluginsFolder->read();
foreach ( $plugins[0] as $plugin ) {
$pluginModels = App::objects($plugin . '.Model');
foreach ($pluginModels as $pluginModel) {
$fullPath = APP.'Plugin'.DS.$plugin.DS."Model".DS.$pluginModel.".php";
$models[$plugin . '.' . $pluginModel] = $fullPath;
}
}
foreach($models as $model => $modelPath) {
if(file_exists($modelPath)) {
$data = file_get_contents($modelPath);
$find = array();
$find[] = "useTable = '";
$find[] = "useTable='";
$find[] = "useTable= '";
$find[] = "useTable ='";
foreach($find as $condition) {
$pos = strrpos($data, $condition);
if($pos !== false ) {
$pos+=(strlen($condition));
$tableName = substr($data, $pos, (strpos($data, "';", $pos)-$pos));
debug($tableName);
}
}
}
}
}
CakePHP v2
In AppController
if (in_array('YOUR_MODEL', $this->uses)) {
//found
}
Related
I can get the not-bind query on with this way :
\DB::enableQueryLog();
$items = OrderItem::where('name', '=', 'test')->get();
$log = \DB::getQueryLog();
print_r($log);
Output is :
(
[0] => Array
(
[query] => select * from "order_items" where "order_items"."name" = ? and "order_items"."deleted_at" is null
[bindings] => Array
(
[0] => test
)
[time] => 0.07
)
)
But what I really need is bind query like this :
select * from "order_items" where "order_items"."name" = 'test' and "order_items"."deleted_at" is null
I know I can do this with raw PHP but is there any solution in laravel core?
Actually I've created one function within helpers.php for same. You can also use same function within your helpers.php file
if (! function_exists('ql'))
{
/**
* Get Query Log
*
* #return array of queries
*/
function ql()
{
$log = \DB::getQueryLog();
$pdo = \DB::connection()->getPdo();
foreach ($log as &$l)
{
$bindings = $l['bindings'];
if (!empty($bindings))
{
foreach ($bindings as $key => $binding)
{
// This regex matches placeholders only, not the question marks,
// nested in quotes, while we iterate through the bindings
// and substitute placeholders by suitable values.
$regex = is_numeric($key)
? "/\?(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/"
: "/:{$key}(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/";
$l['query'] = preg_replace($regex, $pdo->quote($binding), $l['query'], 1);
}
}
}
return $log;
}
}
if (! function_exists('qldd'))
{
/**
* Get Query Log then Dump and Die
*
* #return array of queries
*/
function qldd()
{
dd(ql());
}
}
if (! function_exists('qld'))
{
/**
* Get Query Log then Dump
*
* #return array of queries
*/
function qld()
{
dump(ql());
}
}
Simply place these three functions within your helpers.php file and you can use same as follows:
$items = OrderItem::where('name', '=', 'test')->get();
qldd(); //for dump and die
or you can use
qld(); // for dump only
Here I extended the answer of #blaz
In app\Providers\AppServiceProvider.php
Add this on boot() method
if (env('APP_DEBUG')) {
DB::listen(function($query) {
File::append(
storage_path('/logs/query.log'),
self::queryLog($query->sql, $query->bindings) . "\n\n"
);
});
}
and also added a private method
private function queryLog($sql, $binds)
{
$result = "";
$sql_chunks = explode('?', $sql);
foreach ($sql_chunks as $key => $sql_chunk) {
if (isset($binds[$key])) {
$result .= $sql_chunk . '"' . $binds[$key] . '"';
}
}
$result .= $sql_chunks[count($sql_chunks) -1];
return $result;
}
Yeah, you're right :/
This is a highly requested feature, and i have no idea why its not a part of the framework yet...
This is not the most elegant solution, but you can do something like this:
function getPureSql($sql, $binds) {
$result = "";
$sql_chunks = explode('?', $sql);
foreach ($sql_chunks as $key => $sql_chunk) {
if (isset($binds[$key])) {
$result .= $sql_chunk . '"' . $binds[$key] . '"';
}
}
return $result;
}
$query = OrderItem::where('name', '=', 'test');
$pure_sql_query = getPureSql($query->toSql(), $query->getBindings());
// Or like this:
$data = OrderItem::where('name', '=', 'test')->get();
$log = DB::getQueryLog();
$log = end($log);
$pure_sql_query = getPureSql($log['query'], $log['bindings']);
You can do that with:
OrderItem::where('name', '=', 'test')->toSql();
I'm new to yii framework. I'm just trying to implement Restful APIs. In the simple case scenario (after following some tutorial) I've succeeded with this:
$result = [];
foreach($this->getData() as $record) {
$result[] = $record->getAttributes();
}
return $result;
Note that getData() is a built-in method. Where when trying to use queries for more advanced scenarios, it's done this way:
$attributeNames = 'udid,model,appverionid';
$connection = Yii::app()->db;
$command = $connection->createCommand('select ' . $attributeNames . ' from device');
$models = $command->queryAll();
$attributeNames = explode(',', $attributeNames);
$rows = array();
foreach ($models as $model) {
$row = array();
foreach ($attributeNames as $name) {
$row[$name] = CHtml::value($model, $name);
}
$rows[] = $row;
}
return $rows;
Is that the best practice to return get JSON from queries results, or maybe it can be done in a better way?
Update:
The final response is returned from the following method:
private function sendAjaxResponse(AjaxResponseInterface $interface)
{
$success = count($interface->getErrors()) === 0;
$responseCode = $success ? 200 : 404;
header("content-type: application/json", true, $responseCode);
echo json_encode([
'success' => $success,
'data' => $interface -> getResponseData(),
'errors' => $interface -> getErrors()
]);
Yii::app()->end();
}
And I found out that only these lines are sufficient:
$attributeNames = 'udid,model,appverionid';
$connection = Yii::app()->db;
$command = $connection->createCommand('select ' . $attributeNames . ' from device');
$models = $command->queryAll();
return $models;
The other part (the nested loop) seems for encoding with relations (see here) Then my question is what is encoding with relations and when it is helpful?
The Yii way of creating JSOn data is using CJson library
$query = "'select ' . $attributeNames . ' from device'";
$command = Yii::app()->db->createCommand($query);
$result = $command->queryAll();
$ret = array_values($result);
/* or ...
$ret = array();
foreach ($models as $model) {
$ret = array();
foreach ($attributeNames as $name) {
$row[$name] = CHtml::value($model, $name);
}
$ret[] = $row;
}
*/
echo CJSON::encode($ret);
Yii::app()->end();
I think you simply need to encode the value before return
foreach ($models as $model) {
$row = array();
foreach ($attributeNames as $name) {
$row[$name] = CHtml::value($model, $name);
}
$rows[] = $row;
}
return json_encode($rows);
Very Simple! Just add this line - \Yii::$app->response->format = \yii\web\Response:: FORMAT_JSON; in your method at start and do other code
normally.
for example,
public function actionHospitals()
{
\Yii::$app->response->format = \yii\web\Response:: FORMAT_JSON;
$hospitals = Hospitals::find()->select('speciality')->distinct()->all();
return ['hospitals' => $hospitals];
}
I found this class for threaded comment using php and MySQL:
<?php
class Threaded_comments
{
public $parents = array();
public $children = array();
/**
* #param array $comments
*/
function __construct($comments)
{
foreach ($comments as $comment)
{
if ($comment['parent_id'] === NULL)
{
$this->parents[$comment['id']][] = $comment;
}
else
{
$this->children[$comment['parent_id']][] = $comment;
}
}
}
/**
* #param array $comment
* #param int $depth
*/
private function format_comment($comment, $depth)
{
for ($depth; $depth > 0; $depth--)
{
echo "\t";
}
echo $comment['text'];
echo "\n";
}
/**
* #param array $comment
* #param int $depth
*/
private function print_parent($comment, $depth = 0)
{
foreach ($comment as $c)
{
$this->format_comment($c, $depth);
if (isset($this->children[$c['id']]))
{
$this->print_parent($this->children[$c['id']], $depth + 1);
}
}
}
public function print_comments()
{
foreach ($this->parents as $c)
{
$this->print_parent($c);
}
}
}
this worked with this array:
$comment = array( array('id'=>1, 'parent_id'=>NULL, 'text'=>'Parent'),
array('id'=>2, 'parent_id'=>1, 'text'=>'Child'),
array('id'=>3, 'parent_id'=>2, 'text'=>'Child Third level'),
array('id'=>4, 'parent_id'=>NULL, 'text'=>'Second Parent'),
array('id'=>5, 'parent_id'=>4, 'text'=>'Second Child')
);
and for result:
$tc = new Threaded_comments($comment);
$tc->print_comments();
and result is:
Parent
Child
Child Third level
Second Parent
Second Child
this worked true But for defign comment list(html) I need to add all html code into private function format_comment($comment, $depth). this is not good way and I think need separate html from php class.
EDIT:(this is my page for show/print threaded comment)
function _comments_($id,$type){
$DB_QUERY = mySqli::f("SELECT id,user,email,message,timestamp,parent_id,rfield_1,rfield_2,rfield_3,rfield_4,rfield_5 FROM " . NEWS_COMMENTS . " LEFT JOIN " . NEWS_REVIEWS . " ON " . NEWS_COMMENTS . ".id = " . NEWS_REVIEWS . ".cid WHERE
pid = ? AND type = ? AND approved = 1 ORDER BY timestamp DESC LIMIT 12", $id, $type);
foreach($DB_QUERY as $row){
$commentdata[] = $row;
}
return $commentdata;
}
$comments_list = _comments_('125','book');
foreach($comments_list as $row){
$comments[] = array(
'id' => $row['id'],
'parent_id' => $row['parent_id'],
'name' => $row['user'],
'text' => $row['message'],
'datetime' => $row['timestamp']
);
}
$tc = new Threaded_comments($comments);
$tc->print_comments();
In my page threaded comments show with format_comment and work true. in case i need to design output using separate html from php class.
how do we separate html code from class in this case?!
You're right.
1) Most simple solution is to pass your array to a template. This template doesn't contain logic, but only html and php (e.g. foreach of your your array).
2) For beter separation, use Model-View-Controller pattern:
With this layer construction, each layer has its own responsibilities. Never html in model and controller. Queries in the models only.
Frameworks as Laravel and CodeIgniter have an implementation of this pattern.
I'm developing a code that's supposed to go through all records in a database and run 'save()' on each of them.
The thing is, it's giving me a 'Fatal error: Allowed memory size exhausted' error (see below)
current memory usage: 19592272
current memory usage: 20944968
current memory usage: 22361824
current memory usage: 23649968
.
.
.
current memory usage: 274071160
current memory usage: 275354880
Fatal error: Allowed memory size of 276824064 bytes exhausted (tried to allocate 71 bytes) in/home/lestest/public_html/garratt/phproad/modules/phpr/classes/phpr_extensible.php on line 94
I read this was an issue with any PHP version that's below 5.3 (I'm using PHP 5.3.2). I follow advices from articles like http://paul-m-jones.com/archives/262#comment-940 and https://bugs.php.net/bug.php?id=48781I, and made sure to destruct() and unset() the object after each loop to free the memory, but I don't know why it's still keeping memory. Can anyone point out what should be changed with the code?
Below is my code:
<?php
class ProductSave_Products extends Backend_Controller {
public $implement = 'Db_FormBehavior, Db_ListBehavior';
public $form_model_class = 'ProductSave_Model';
public function index()
{
$this->app_module_name = 'Products';
$this->app_page_title = 'Save';
$obj = new ProductSave_Model();
$ids = Db_DbHelper::queryArray('select id from shop_product');
foreach($ids as $row)
{
$product = Shop_Product::create()->find($row['id']);
$product->save();
$product->__destruct();
unset($product);
print "current memory usage: ". memory_get_usage() . '<br/><br/>';
}
}
}
?>
Here is my Db_Helper class code:
<?php
class Db_DbHelper
{
protected static $driver = false;
public static function listTables()
{
return Db_Sql::create()->fetchCol('show tables');
}
public static function tableExists($tableName)
{
$tables = self::listTables();
return in_array($tableName, $tables);
}
public static function executeSqlScript($filePath, $separator = ';')
{
$fileContents = file_get_contents($filePath);
$fileContents = str_replace( "\r\n", "\n", $fileContents );
$statements = explode( $separator."\n", $fileContents );
$sql = Db_Sql::create();
foreach ( $statements as $statement )
{
if ( strlen(trim($statement)) )
$sql->execute($statement);
}
}
public static function scalar($sql, $bind = array())
{
return Db_Sql::create()->fetchOne($sql, $bind);
}
public static function scalarArray($sql, $bind = array())
{
$values = self::queryArray($sql, $bind);
$result = array();
foreach ($values as $value)
{
$keys = array_keys($value);
if ($keys)
$result[] = $value[$keys[0]];
}
return $result;
}
public static function query($sql, $bind = array())
{
$obj = Db_Sql::create();
return $obj->query($obj->prepare($sql, $bind));
}
public static function fetch_next($resource)
{
return self::driver()->fetch($resource);
}
public static function free_result($resource)
{
self::driver()->free_query_result($resource);
}
public static function queryArray($sql, $bind = array())
{
return Db_Sql::create()->fetchAll($sql, $bind);
}
public static function objectArray($sql, $bind = array())
{
$recordSet = self::queryArray($sql, $bind);
$result = array();
foreach ($recordSet as $record)
$result[] = (object)$record;
return $result;
}
public static function object($sql, $bind = array())
{
$result = self::objectArray($sql, $bind);
if (!count($result))
return null;
return $result[0];
}
public static function getTableStruct( $tableName )
{
$sql = Db_Sql::create();
$result = $sql->query($sql->prepare("SHOW CREATE TABLE `$tableName`"));
return $sql->driver()->fetch($result, 1);
}
public static function getTableDump( $tableName, $fp = null, $separator = ';' )
{
$sql = Db_Sql::create();
$qr = $sql->query("SELECT * FROM `$tableName`");
$result = null;
$columnNames = null;
while ($row = $sql->driver()->fetch($qr))
{
if ( $columnNames === null )
$columnNames = '`'.implode( '`,`', array_keys($row) ).'`';
if (!$fp)
{
$result .= "INSERT INTO `$tableName`(".$columnNames.") VALUES (";
$result .= $sql->quote( array_values($row) );
$result .= ")".$separator."\n";
} else
{
fwrite($fp, "INSERT INTO `$tableName`(".$columnNames.") VALUES (");
fwrite($fp, $sql->quote( array_values($row) ));
fwrite($fp, ")".$separator."\n");
}
}
return $result;
}
public static function createDbDump($path, $options = array())
{
#set_time_limit(600);
$tables_to_ignore = array_key_exists('ignore', $options) ? $options['ignore'] : array();
$separator = array_key_exists('separator', $options) ? $options['separator'] : ';';
$fp = #fopen($path, "w");
if (!$fp)
throw new Phpr_SystemException('Error opening file for writing: '.$path);
$sql = Db_Sql::create();
try
{
fwrite($fp, "SET NAMES utf8".$separator."\n\n");
$tables = self::listTables();
foreach ($tables as $index=>$table)
{
if (in_array($table, $tables_to_ignore))
continue;
fwrite($fp, '# TABLE '.$table."\n#\n");
fwrite($fp, 'DROP TABLE IF EXISTS `'.$table."`".$separator."\n");
fwrite($fp, self::getTableStruct($table).$separator."\n\n" );
self::getTableDump($table, $fp, $separator);
$sql->driver()->reconnect();
}
#fclose($fp);
#chmod($path, Phpr_Files::getFilePermissions());
}
catch (Exception $ex)
{
#fclose($fp);
throw $ex;
}
}
/**
* Generates an unique column value
* #param Db_ActiveRecord $model A model to generate value for
* #param string $column_name A name of a column
* #param string $base_value A base value of the column. The unique value will be generated
* by appending the 'copy_1', 'copy_N' string to the base value.
* #param bool $case_sensitive Specifies whether function should perform a case-sensitive search
* #return string
*/
public static function getUniqueColumnValue($model, $column_name, $base_value, $case_sensitive = false)
{
$base_value = trim($base_value);
$base_value = preg_replace('/_copy_[0-9]+$/', '', $base_value);
$column_value = $base_value;
$counter = 1;
$table_name = $model->table_name;
$query = $case_sensitive ?
"select count(*) from $table_name where $column_name=:test_value" :
"select count(*) from $table_name where lower($column_name)=lower(:test_value)";
while (self::scalar("select count(*) from $table_name where $column_name=:test_value", array(
'test_value'=>$column_value
)))
{
$column_value = $base_value.'_copy_'.$counter;
$counter++;
}
return $column_value;
}
/**
* Creates a SQL query string for searching specified fields for specified words or phrases
* #param string $query Search query
* #param array|array $fields A list of fields to search in. A single field can be specified as a string
* #param int $min_word_length Allows to ignore words with length less than the specified
* #return string Returns a string
*/
public static function formatSearchQuery($query, $fields, $min_word_length = null)
{
if (!is_array($fields))
$fields = array($fields);
$words = Core_String::split_to_words($query);
$word_queries = array();
foreach ($words as $word)
{
if (!strlen($word))
continue;
if ($min_word_length && mb_strlen($word) < $min_word_length)
continue;
$word = trim(mb_strtolower($word));
$word_queries[] = '%1$s like \'%2$s'.self::driver()->escape($word).'%2$s\'';
}
$field_queries = array();
foreach ($fields as $field)
{
if ($word_queries)
$field_queries[] = '('.sprintf(implode(' and ', $word_queries), $field, '%').')';
}
if (!$field_queries)
return '1=1';
return '('.implode(' or ', $field_queries).')';
}
public static function reset_driver()
{
self::$driver = false;
}
public static function driver()
{
if (!self::$driver)
{
$sql = Db_Sql::create();
return self::$driver = $sql->driver();
}
return self::$driver;
}
public static function escape($str)
{
return self::driver()->escape($str);
}
}
?>
Thank you
I want to list all the modules , controllers and actions included in my project for acl purpose. But i dont know how can i fetch such information from zf2. Anyone has an idea of how to make this work? ThankYou :)
Try this,
$manager = $this->getServiceLocator()->get('ModuleManager');
$modules = $manager->getLoadedModules();
$loadedModules = array_keys($modules);
$skipActionsList = array('notFoundAction', 'getMethodFromAction');
foreach ($loadedModules as $loadedModule) {
$moduleClass = '\\' .$loadedModule . '\Module';
$moduleObject = new $moduleClass;
$config = $moduleObject->getConfig();
$controllers = $config['controllers']['invokables'];
foreach ($controllers as $key => $moduleClass) {
$tmpArray = get_class_methods($moduleClass);
$controllerActions = array();
foreach ($tmpArray as $action) {
if (substr($action, strlen($action)-6) === 'Action' && !in_array($action, $skipActionsList)) {
$controllerActions[] = $action;
}
}
echo $loadedModule . "\n";
echo $moduleClass . "\n";
print_r($controllerActions);
}
}
You can get a array of the loaded modules from the ModuleManager.
$moduleManager = $serviceManager->get('ModuleManager');
$loadedModules = $moduleManager->getLoadedModules();
For controllers you can retrieve them from the ControllerManager.
$controllerManager = $this->getServiceLocator()->get('ControllerLoader');
foreach ($controllerManager->getCanonicalNames() as $alias) {
$controller = $controllerManager->get($alias); // This will get you the controller instance
}
ZF2 doesn't have any convenience methods built in to retrieve all the controller actions. Using some reflection would be your best bet imo.
$reflection = new \ReflectionClass($controller);
$actions = array();
foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
$methodName = $method->getName();
if ($methodName == 'getMethodFromAction') {
continue;
}
if (substr_compare($methodName, 'Action', -strlen('Action')) === 0) {
$actions[] = substr($methodName, 0, strlen($methodName) - 6);
}
}
var_dump($actions);