2

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:

  1. custom table names that are different to the table object name
  2. 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.

mixable
  • 1,068
  • 2
  • 12
  • 42
  • 1
    `I need a list of all table objects that use a behavior X` - why? I'm sure there are better ways to do whatever you try. – floriank Aug 25 '17 at 13:18
  • Please have a look at the updated question, I added some more background information. – mixable Aug 25 '17 at 13:27
  • 1
    Why do you need to update *all*? We do an index update for the table in question when `Model.afterSave` is triggered. A behavior does this. The behavior also checks for a method to get the data, if not present it falls back to Table::get(). The plugin is open source by the way but uses Elastic Search https://github.com/World-Architects/cakephp-elastic-index for indexing the data. – floriank Aug 25 '17 at 13:46
  • The _update all_ is required for the initial creation of the search index or if a table structure changes and the index needs to be recreated. Your plugin looks great, thanks! Its behavior is similar to my solution. Did I understand it right, that your behavior recreates the index for a table each time a single data set is saved? Or just the index data for this single data set? – mixable Aug 25 '17 at 17:42
  • No because this would be silly because it's slow and takes time. It makes absolutely no sense to re-create the whole index. We have a shell (included in the plugin) and specify via config a list of tables that can be indexed. That's how we re-create the index but only do that after a schema change and only on a new index and then switch the index *after* the indexing is complete. You don't rebuild a whole index at run time. – floriank Aug 27 '17 at 10:02
  • Yes, that would be very slow. That's why I was asking. In my case, updating the whole index is also done manually and not for every single _afterSave_ call. Using a config for the reindexing is the thing I want to bypass. By attaching the _SearchableBehavior_ you have all the information available to know which _Model_ can be indexed. So you don't need that config list... but you need a solution for my question ;) – mixable Aug 28 '17 at 07:44

3 Answers3

1

    $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);

  • 1
    Welcome to Stack Overflow and thank you so much for your willingness to help others! However, when posting an answer, please be sure to include some information as to why your code works. What does it do? How does it relate to the original question? Why is this solution a **good** solution? – Zephyr Oct 08 '19 at 16:49
0

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.

floriank
  • 25,546
  • 9
  • 42
  • 66
  • Thanks for your answer. I will check the `Model.initialize` solution, maybe this works. I added some additional information to my question to give you an idea what I want to do. – mixable Aug 25 '17 at 13:38
0

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.

Lars Ebert
  • 3,487
  • 2
  • 24
  • 46