@@ -12,48 +12,48 @@ |
||
12 | 12 | class GenerateCommand extends Command |
13 | 13 | { |
14 | 14 | |
15 | - /** |
|
16 | - * @var ConfigurationInterface |
|
17 | - */ |
|
18 | - private $configuration; |
|
15 | + /** |
|
16 | + * @var ConfigurationInterface |
|
17 | + */ |
|
18 | + private $configuration; |
|
19 | 19 | |
20 | - public function __construct(ConfigurationInterface $configuration) |
|
21 | - { |
|
22 | - parent::__construct(); |
|
23 | - $this->configuration = $configuration; |
|
24 | - } |
|
20 | + public function __construct(ConfigurationInterface $configuration) |
|
21 | + { |
|
22 | + parent::__construct(); |
|
23 | + $this->configuration = $configuration; |
|
24 | + } |
|
25 | 25 | |
26 | - protected function configure() |
|
27 | - { |
|
28 | - $this->setName('tdbm:generate') |
|
29 | - ->setDescription('Generates DAOs and beans.') |
|
30 | - ->setHelp('Use this command to generate or regenerate the DAOs and beans for your project.') |
|
31 | - ; |
|
32 | - } |
|
26 | + protected function configure() |
|
27 | + { |
|
28 | + $this->setName('tdbm:generate') |
|
29 | + ->setDescription('Generates DAOs and beans.') |
|
30 | + ->setHelp('Use this command to generate or regenerate the DAOs and beans for your project.') |
|
31 | + ; |
|
32 | + } |
|
33 | 33 | |
34 | - protected function execute(InputInterface $input, OutputInterface $output) |
|
35 | - { |
|
36 | - // TODO: externalize composer.json file for autoloading (no more parameters for generateAllDaosAndBeans) |
|
34 | + protected function execute(InputInterface $input, OutputInterface $output) |
|
35 | + { |
|
36 | + // TODO: externalize composer.json file for autoloading (no more parameters for generateAllDaosAndBeans) |
|
37 | 37 | |
38 | - $alteredConf = new AlteredConfiguration($this->configuration); |
|
38 | + $alteredConf = new AlteredConfiguration($this->configuration); |
|
39 | 39 | |
40 | 40 | |
41 | - $loggers = [ new ConsoleLogger($output) ]; |
|
41 | + $loggers = [ new ConsoleLogger($output) ]; |
|
42 | 42 | |
43 | - $logger = $alteredConf->getLogger(); |
|
44 | - if ($logger) { |
|
45 | - $loggers[] = $logger; |
|
46 | - } |
|
43 | + $logger = $alteredConf->getLogger(); |
|
44 | + if ($logger) { |
|
45 | + $loggers[] = $logger; |
|
46 | + } |
|
47 | 47 | |
48 | - $multiLogger = new MultiLogger($loggers); |
|
48 | + $multiLogger = new MultiLogger($loggers); |
|
49 | 49 | |
50 | - $alteredConf->setLogger($multiLogger); |
|
50 | + $alteredConf->setLogger($multiLogger); |
|
51 | 51 | |
52 | - $multiLogger->notice('Starting regenerating DAOs and beans'); |
|
52 | + $multiLogger->notice('Starting regenerating DAOs and beans'); |
|
53 | 53 | |
54 | - $tdbmService = new TDBMService($this->configuration); |
|
55 | - $tdbmService->generateAllDaosAndBeans(); |
|
54 | + $tdbmService = new TDBMService($this->configuration); |
|
55 | + $tdbmService->generateAllDaosAndBeans(); |
|
56 | 56 | |
57 | - $multiLogger->notice('Finished regenerating DAOs and beans'); |
|
58 | - } |
|
57 | + $multiLogger->notice('Finished regenerating DAOs and beans'); |
|
58 | + } |
|
59 | 59 | } |
@@ -19,134 +19,134 @@ |
||
19 | 19 | */ |
20 | 20 | class TdbmController extends AbstractMoufInstanceController |
21 | 21 | { |
22 | - /** |
|
23 | - * @var HtmlBlock |
|
24 | - */ |
|
25 | - public $content; |
|
26 | - |
|
27 | - protected $daoNamespace; |
|
28 | - protected $beanNamespace; |
|
29 | - protected $daoFactoryInstanceName; |
|
30 | - protected $autoloadDetected; |
|
31 | - ///protected $storeInUtc; |
|
32 | - protected $useCustomComposer; |
|
33 | - protected $composerFile; |
|
34 | - |
|
35 | - /** |
|
36 | - * Admin page used to display the DAO generation form. |
|
37 | - * |
|
38 | - * @Action |
|
39 | - */ |
|
40 | - public function defaultAction($name, $selfedit = 'false') |
|
41 | - { |
|
42 | - $this->initController($name, $selfedit); |
|
43 | - |
|
44 | - // Fill variables |
|
45 | - $this->daoNamespace = self::getFromConfiguration($this->moufManager, $name, 'daoNamespace'); |
|
46 | - $this->beanNamespace = self::getFromConfiguration($this->moufManager, $name, 'beanNamespace'); |
|
47 | - $this->daoFactoryInstanceName = self::getFromConfiguration($this->moufManager, $name, 'daoFactoryInstanceName'); |
|
48 | - //$this->storeInUtc = self::getFromConfiguration($this->moufManager, $name, 'storeInUtc'); |
|
49 | - $pathFinder = self::getFromConfiguration($this->moufManager, $name, 'pathFinder'); |
|
50 | - if ($pathFinder !== null) { |
|
51 | - $this->composerFile = $pathFinder->getConstructorArgumentProperty('composerFile')->getValue(); |
|
52 | - } else { |
|
53 | - $this->composerFile = null; |
|
54 | - } |
|
55 | - $this->useCustomComposer = $this->composerFile ? true : false; |
|
56 | - |
|
57 | - if ($this->daoNamespace == null && $this->beanNamespace == null) { |
|
58 | - $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json'); |
|
59 | - |
|
60 | - $autoloadNamespaces = $classNameMapper->getManagedNamespaces(); |
|
61 | - if ($autoloadNamespaces) { |
|
62 | - $this->autoloadDetected = true; |
|
63 | - $rootNamespace = $autoloadNamespaces[0]; |
|
64 | - $this->daoNamespace = $rootNamespace.'Dao'; |
|
65 | - $this->beanNamespace = $rootNamespace.'Dao\\Bean'; |
|
66 | - } else { |
|
67 | - $this->autoloadDetected = false; |
|
68 | - $this->daoNamespace = 'YourApplication\\Dao'; |
|
69 | - $this->beanNamespace = 'YourApplication\\Dao\\Bean'; |
|
70 | - } |
|
71 | - } else { |
|
72 | - $this->autoloadDetected = true; |
|
73 | - } |
|
74 | - |
|
75 | - $this->content->addFile(__DIR__.'/../../../../views/tdbmGenerate.php', $this); |
|
76 | - $this->template->toHtml(); |
|
77 | - } |
|
78 | - |
|
79 | - /** |
|
80 | - * This action generates the DAOs and Beans for the TDBM service passed in parameter. |
|
81 | - * |
|
82 | - * @Action |
|
83 | - * |
|
84 | - * @param string $name |
|
85 | - * @param bool $selfedit |
|
86 | - */ |
|
87 | - public function generate($name, $daonamespace, $beannamespace, $daofactoryinstancename, /*$storeInUtc = 0,*/ $selfedit = 'false', $useCustomComposer = false, $composerFile = '') |
|
88 | - { |
|
89 | - $this->initController($name, $selfedit); |
|
90 | - |
|
91 | - self::generateDaos($this->moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit, /*$storeInUtc,*/ $useCustomComposer, $composerFile); |
|
92 | - |
|
93 | - // TODO: better: we should redirect to a screen that list the number of DAOs generated, etc... |
|
94 | - header('Location: '.ROOT_URL.'ajaxinstance/?name='.urlencode($name).'&selfedit='.$selfedit); |
|
95 | - } |
|
96 | - |
|
97 | - /** |
|
98 | - * This function generates the DAOs and Beans for the TDBM service passed in parameter. |
|
99 | - * |
|
100 | - * @param MoufManager $moufManager |
|
101 | - * @param string $name |
|
102 | - * @param string $daonamespace |
|
103 | - * @param string $beannamespace |
|
104 | - * @param string $selfedit |
|
105 | - * |
|
106 | - * @throws \Mouf\MoufException |
|
107 | - */ |
|
108 | - public static function generateDaos(MoufManager $moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit = 'false', /*$storeInUtc = null,*/ $useCustomComposer = null, $composerFile = null) |
|
109 | - { |
|
110 | - self::setInConfiguration($moufManager, $name, 'daoNamespace', $daonamespace); |
|
111 | - self::setInConfiguration($moufManager, $name, 'beanNamespace', $beannamespace); |
|
112 | - self::setInConfiguration($moufManager, $name, 'daoFactoryInstanceName', $daofactoryinstancename); |
|
113 | - //self::setInConfiguration($moufManager, $name, 'storeInUtc', $storeInUtc); |
|
114 | - if ($useCustomComposer) { |
|
115 | - $pathFinder = $moufManager->createInstance(PathFinder::class); |
|
116 | - $pathFinder->getConstructorArgumentProperty('composerFile')->setValue($composerFile); |
|
117 | - self::setInConfiguration($moufManager, $name, 'pathFinder', $pathFinder); |
|
118 | - } else { |
|
119 | - self::setInConfiguration($moufManager, $name, 'pathFinder', null); |
|
120 | - } |
|
121 | - // Let's rewrite before calling the DAO generator |
|
122 | - $moufManager->rewriteMouf(); |
|
123 | - |
|
124 | - |
|
125 | - $tdbmService = new InstanceProxy($name); |
|
126 | - /* @var $tdbmService TDBMService */ |
|
127 | - $tdbmService->generateAllDaosAndBeans(); |
|
128 | - } |
|
129 | - |
|
130 | - private static function getConfigurationDescriptor(MoufManager $moufManager, string $tdbmInstanceName) |
|
131 | - { |
|
132 | - return $moufManager->getInstanceDescriptor($tdbmInstanceName)->getConstructorArgumentProperty('configuration')->getValue(); |
|
133 | - } |
|
134 | - |
|
135 | - private static function getFromConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property) |
|
136 | - { |
|
137 | - $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName); |
|
138 | - if ($configuration === null) { |
|
139 | - throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.'); |
|
140 | - } |
|
141 | - return $configuration->getProperty($property)->getValue(); |
|
142 | - } |
|
143 | - |
|
144 | - private static function setInConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property, ?string $value) |
|
145 | - { |
|
146 | - $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName); |
|
147 | - if ($configuration === null) { |
|
148 | - throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.'); |
|
149 | - } |
|
150 | - $configuration->getProperty($property)->setValue($value); |
|
151 | - } |
|
22 | + /** |
|
23 | + * @var HtmlBlock |
|
24 | + */ |
|
25 | + public $content; |
|
26 | + |
|
27 | + protected $daoNamespace; |
|
28 | + protected $beanNamespace; |
|
29 | + protected $daoFactoryInstanceName; |
|
30 | + protected $autoloadDetected; |
|
31 | + ///protected $storeInUtc; |
|
32 | + protected $useCustomComposer; |
|
33 | + protected $composerFile; |
|
34 | + |
|
35 | + /** |
|
36 | + * Admin page used to display the DAO generation form. |
|
37 | + * |
|
38 | + * @Action |
|
39 | + */ |
|
40 | + public function defaultAction($name, $selfedit = 'false') |
|
41 | + { |
|
42 | + $this->initController($name, $selfedit); |
|
43 | + |
|
44 | + // Fill variables |
|
45 | + $this->daoNamespace = self::getFromConfiguration($this->moufManager, $name, 'daoNamespace'); |
|
46 | + $this->beanNamespace = self::getFromConfiguration($this->moufManager, $name, 'beanNamespace'); |
|
47 | + $this->daoFactoryInstanceName = self::getFromConfiguration($this->moufManager, $name, 'daoFactoryInstanceName'); |
|
48 | + //$this->storeInUtc = self::getFromConfiguration($this->moufManager, $name, 'storeInUtc'); |
|
49 | + $pathFinder = self::getFromConfiguration($this->moufManager, $name, 'pathFinder'); |
|
50 | + if ($pathFinder !== null) { |
|
51 | + $this->composerFile = $pathFinder->getConstructorArgumentProperty('composerFile')->getValue(); |
|
52 | + } else { |
|
53 | + $this->composerFile = null; |
|
54 | + } |
|
55 | + $this->useCustomComposer = $this->composerFile ? true : false; |
|
56 | + |
|
57 | + if ($this->daoNamespace == null && $this->beanNamespace == null) { |
|
58 | + $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json'); |
|
59 | + |
|
60 | + $autoloadNamespaces = $classNameMapper->getManagedNamespaces(); |
|
61 | + if ($autoloadNamespaces) { |
|
62 | + $this->autoloadDetected = true; |
|
63 | + $rootNamespace = $autoloadNamespaces[0]; |
|
64 | + $this->daoNamespace = $rootNamespace.'Dao'; |
|
65 | + $this->beanNamespace = $rootNamespace.'Dao\\Bean'; |
|
66 | + } else { |
|
67 | + $this->autoloadDetected = false; |
|
68 | + $this->daoNamespace = 'YourApplication\\Dao'; |
|
69 | + $this->beanNamespace = 'YourApplication\\Dao\\Bean'; |
|
70 | + } |
|
71 | + } else { |
|
72 | + $this->autoloadDetected = true; |
|
73 | + } |
|
74 | + |
|
75 | + $this->content->addFile(__DIR__.'/../../../../views/tdbmGenerate.php', $this); |
|
76 | + $this->template->toHtml(); |
|
77 | + } |
|
78 | + |
|
79 | + /** |
|
80 | + * This action generates the DAOs and Beans for the TDBM service passed in parameter. |
|
81 | + * |
|
82 | + * @Action |
|
83 | + * |
|
84 | + * @param string $name |
|
85 | + * @param bool $selfedit |
|
86 | + */ |
|
87 | + public function generate($name, $daonamespace, $beannamespace, $daofactoryinstancename, /*$storeInUtc = 0,*/ $selfedit = 'false', $useCustomComposer = false, $composerFile = '') |
|
88 | + { |
|
89 | + $this->initController($name, $selfedit); |
|
90 | + |
|
91 | + self::generateDaos($this->moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit, /*$storeInUtc,*/ $useCustomComposer, $composerFile); |
|
92 | + |
|
93 | + // TODO: better: we should redirect to a screen that list the number of DAOs generated, etc... |
|
94 | + header('Location: '.ROOT_URL.'ajaxinstance/?name='.urlencode($name).'&selfedit='.$selfedit); |
|
95 | + } |
|
96 | + |
|
97 | + /** |
|
98 | + * This function generates the DAOs and Beans for the TDBM service passed in parameter. |
|
99 | + * |
|
100 | + * @param MoufManager $moufManager |
|
101 | + * @param string $name |
|
102 | + * @param string $daonamespace |
|
103 | + * @param string $beannamespace |
|
104 | + * @param string $selfedit |
|
105 | + * |
|
106 | + * @throws \Mouf\MoufException |
|
107 | + */ |
|
108 | + public static function generateDaos(MoufManager $moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit = 'false', /*$storeInUtc = null,*/ $useCustomComposer = null, $composerFile = null) |
|
109 | + { |
|
110 | + self::setInConfiguration($moufManager, $name, 'daoNamespace', $daonamespace); |
|
111 | + self::setInConfiguration($moufManager, $name, 'beanNamespace', $beannamespace); |
|
112 | + self::setInConfiguration($moufManager, $name, 'daoFactoryInstanceName', $daofactoryinstancename); |
|
113 | + //self::setInConfiguration($moufManager, $name, 'storeInUtc', $storeInUtc); |
|
114 | + if ($useCustomComposer) { |
|
115 | + $pathFinder = $moufManager->createInstance(PathFinder::class); |
|
116 | + $pathFinder->getConstructorArgumentProperty('composerFile')->setValue($composerFile); |
|
117 | + self::setInConfiguration($moufManager, $name, 'pathFinder', $pathFinder); |
|
118 | + } else { |
|
119 | + self::setInConfiguration($moufManager, $name, 'pathFinder', null); |
|
120 | + } |
|
121 | + // Let's rewrite before calling the DAO generator |
|
122 | + $moufManager->rewriteMouf(); |
|
123 | + |
|
124 | + |
|
125 | + $tdbmService = new InstanceProxy($name); |
|
126 | + /* @var $tdbmService TDBMService */ |
|
127 | + $tdbmService->generateAllDaosAndBeans(); |
|
128 | + } |
|
129 | + |
|
130 | + private static function getConfigurationDescriptor(MoufManager $moufManager, string $tdbmInstanceName) |
|
131 | + { |
|
132 | + return $moufManager->getInstanceDescriptor($tdbmInstanceName)->getConstructorArgumentProperty('configuration')->getValue(); |
|
133 | + } |
|
134 | + |
|
135 | + private static function getFromConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property) |
|
136 | + { |
|
137 | + $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName); |
|
138 | + if ($configuration === null) { |
|
139 | + throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.'); |
|
140 | + } |
|
141 | + return $configuration->getProperty($property)->getValue(); |
|
142 | + } |
|
143 | + |
|
144 | + private static function setInConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property, ?string $value) |
|
145 | + { |
|
146 | + $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName); |
|
147 | + if ($configuration === null) { |
|
148 | + throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.'); |
|
149 | + } |
|
150 | + $configuration->getProperty($property)->setValue($value); |
|
151 | + } |
|
152 | 152 | } |
@@ -21,219 +21,219 @@ |
||
21 | 21 | */ |
22 | 22 | class TdbmInstallController extends Controller |
23 | 23 | { |
24 | - /** |
|
25 | - * @var HtmlBlock |
|
26 | - */ |
|
27 | - public $content; |
|
28 | - |
|
29 | - public $selfedit; |
|
30 | - |
|
31 | - /** |
|
32 | - * The active MoufManager to be edited/viewed. |
|
33 | - * |
|
34 | - * @var MoufManager |
|
35 | - */ |
|
36 | - public $moufManager; |
|
37 | - |
|
38 | - /** |
|
39 | - * The template used by the main page for mouf. |
|
40 | - * |
|
41 | - * @Property |
|
42 | - * @Compulsory |
|
43 | - * |
|
44 | - * @var TemplateInterface |
|
45 | - */ |
|
46 | - public $template; |
|
47 | - |
|
48 | - /** |
|
49 | - * Displays the first install screen. |
|
50 | - * |
|
51 | - * @Action |
|
52 | - * @Logged |
|
53 | - * |
|
54 | - * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
55 | - */ |
|
56 | - public function defaultAction($selfedit = 'false') |
|
57 | - { |
|
58 | - $this->selfedit = $selfedit; |
|
59 | - |
|
60 | - if ($selfedit == 'true') { |
|
61 | - $this->moufManager = MoufManager::getMoufManager(); |
|
62 | - } else { |
|
63 | - $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
64 | - } |
|
65 | - |
|
66 | - $this->content->addFile(dirname(__FILE__).'/../../../../views/installStep1.php', $this); |
|
67 | - $this->template->toHtml(); |
|
68 | - } |
|
69 | - |
|
70 | - /** |
|
71 | - * Skips the install process. |
|
72 | - * |
|
73 | - * @Action |
|
74 | - * @Logged |
|
75 | - * |
|
76 | - * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
77 | - */ |
|
78 | - public function skip($selfedit = 'false') |
|
79 | - { |
|
80 | - InstallUtils::continueInstall($selfedit == 'true'); |
|
81 | - } |
|
82 | - |
|
83 | - protected $daoNamespace; |
|
84 | - protected $beanNamespace; |
|
85 | - protected $autoloadDetected; |
|
86 | - //protected $storeInUtc; |
|
87 | - protected $useCustomComposer = false; |
|
88 | - protected $composerFile; |
|
89 | - |
|
90 | - /** |
|
91 | - * Displays the second install screen. |
|
92 | - * |
|
93 | - * @Action |
|
94 | - * @Logged |
|
95 | - * |
|
96 | - * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
97 | - */ |
|
98 | - public function configure($selfedit = 'false') |
|
99 | - { |
|
100 | - $this->selfedit = $selfedit; |
|
101 | - |
|
102 | - if ($selfedit == 'true') { |
|
103 | - $this->moufManager = MoufManager::getMoufManager(); |
|
104 | - } else { |
|
105 | - $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
106 | - } |
|
107 | - |
|
108 | - // Let's start by performing basic checks about the instances we assume to exist. |
|
109 | - if (!$this->moufManager->instanceExists('dbalConnection')) { |
|
110 | - $this->displayErrorMsg("The TDBM install process assumes your database connection instance is already created, and that the name of this instance is 'dbalConnection'. Could not find the 'dbalConnection' instance."); |
|
111 | - |
|
112 | - return; |
|
113 | - } |
|
114 | - |
|
115 | - if ($this->moufManager->has('tdbmConfiguration')) { |
|
116 | - $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration'); |
|
117 | - |
|
118 | - $this->beanNamespace = $tdbmConfiguration->getConstructorArgumentProperty('beanNamespace')->getValue(); |
|
119 | - $this->daoNamespace = $tdbmConfiguration->getConstructorArgumentProperty('daoNamespace')->getValue(); |
|
120 | - } else { |
|
121 | - // Old TDBM 4.2 fallback |
|
122 | - $this->daoNamespace = $this->moufManager->getVariable('tdbmDefaultDaoNamespace_tdbmService'); |
|
123 | - $this->beanNamespace = $this->moufManager->getVariable('tdbmDefaultBeanNamespace_tdbmService'); |
|
124 | - } |
|
125 | - |
|
126 | - if ($this->daoNamespace == null && $this->beanNamespace == null) { |
|
127 | - $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json'); |
|
128 | - |
|
129 | - $autoloadNamespaces = $classNameMapper->getManagedNamespaces(); |
|
130 | - if ($autoloadNamespaces) { |
|
131 | - $this->autoloadDetected = true; |
|
132 | - $rootNamespace = $autoloadNamespaces[0]; |
|
133 | - $this->daoNamespace = $rootNamespace.'Dao'; |
|
134 | - $this->beanNamespace = $rootNamespace.'Model'; |
|
135 | - } else { |
|
136 | - $this->autoloadDetected = false; |
|
137 | - $this->daoNamespace = 'YourApplication\\Dao'; |
|
138 | - $this->beanNamespace = 'YourApplication\\Model'; |
|
139 | - } |
|
140 | - } else { |
|
141 | - $this->autoloadDetected = true; |
|
142 | - } |
|
143 | - $this->defaultPath = true; |
|
144 | - $this->storePath = ''; |
|
145 | - |
|
146 | - $this->castDatesToDateTime = true; |
|
147 | - |
|
148 | - $this->content->addFile(__DIR__.'/../../../../views/installStep2.php', $this); |
|
149 | - $this->template->toHtml(); |
|
150 | - } |
|
151 | - |
|
152 | - /** |
|
153 | - * This action generates the TDBM instance, then the DAOs and Beans. |
|
154 | - * |
|
155 | - * @Action |
|
156 | - * |
|
157 | - * @param string $daonamespace |
|
158 | - * @param string $beannamespace |
|
159 | - * @param string $selfedit |
|
160 | - * |
|
161 | - * @throws \Mouf\MoufException |
|
162 | - */ |
|
163 | - public function generate($daonamespace, $beannamespace, /*$storeInUtc = 0,*/ $selfedit = 'false', $defaultPath = false, $storePath = '') |
|
164 | - { |
|
165 | - $this->selfedit = $selfedit; |
|
166 | - |
|
167 | - if ($selfedit == 'true') { |
|
168 | - $this->moufManager = MoufManager::getMoufManager(); |
|
169 | - } else { |
|
170 | - $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
171 | - } |
|
172 | - |
|
173 | - $doctrineCache = $this->moufManager->getInstanceDescriptor('defaultDoctrineCache'); |
|
174 | - |
|
175 | - $migratingFrom42 = false; |
|
176 | - if ($this->moufManager->has('tdbmService') && !$this->moufManager->has('tdbmConfiguration')) { |
|
177 | - $migratingFrom42 = true; |
|
178 | - } |
|
179 | - |
|
180 | - $namingStrategy = InstallUtils::getOrCreateInstance('namingStrategy', DefaultNamingStrategy::class, $this->moufManager); |
|
181 | - if ($migratingFrom42) { |
|
182 | - // Let's setup the naming strategy for compatibility |
|
183 | - $namingStrategy->getSetterProperty('setBeanPrefix')->setValue(''); |
|
184 | - $namingStrategy->getSetterProperty('setBeanSuffix')->setValue('Bean'); |
|
185 | - $namingStrategy->getSetterProperty('setBaseBeanPrefix')->setValue(''); |
|
186 | - $namingStrategy->getSetterProperty('setBaseBeanSuffix')->setValue('BaseBean'); |
|
187 | - $namingStrategy->getSetterProperty('setDaoPrefix')->setValue(''); |
|
188 | - $namingStrategy->getSetterProperty('setDaoSuffix')->setValue('Dao'); |
|
189 | - $namingStrategy->getSetterProperty('setBaseDaoPrefix')->setValue(''); |
|
190 | - $namingStrategy->getSetterProperty('setBaseDaoSuffix')->setValue('BaseDao'); |
|
191 | - } |
|
192 | - |
|
193 | - if (!$this->moufManager->instanceExists('tdbmConfiguration')) { |
|
194 | - $moufListener = InstallUtils::getOrCreateInstance(MoufDiListener::class, MoufDiListener::class, $this->moufManager); |
|
195 | - |
|
196 | - $tdbmConfiguration = $this->moufManager->createInstance(MoufConfiguration::class)->setName('tdbmConfiguration'); |
|
197 | - $tdbmConfiguration->getConstructorArgumentProperty('connection')->setValue($this->moufManager->getInstanceDescriptor('dbalConnection')); |
|
198 | - $tdbmConfiguration->getConstructorArgumentProperty('cache')->setValue($doctrineCache); |
|
199 | - $tdbmConfiguration->getConstructorArgumentProperty('namingStrategy')->setValue($namingStrategy); |
|
200 | - $tdbmConfiguration->getProperty('daoFactoryInstanceName')->setValue('daoFactory'); |
|
201 | - $tdbmConfiguration->getConstructorArgumentProperty('generatorListeners')->setValue([$moufListener]); |
|
202 | - |
|
203 | - // Let's also delete the tdbmService if migrating versions <= 4.2 |
|
204 | - if ($migratingFrom42) { |
|
205 | - $this->moufManager->removeComponent('tdbmService'); |
|
206 | - } |
|
207 | - } else { |
|
208 | - $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration'); |
|
209 | - } |
|
210 | - |
|
211 | - if (!$this->moufManager->instanceExists('tdbmService')) { |
|
212 | - $tdbmService = $this->moufManager->createInstance('Mouf\\Database\\TDBM\\TDBMService')->setName('tdbmService'); |
|
213 | - $tdbmService->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration); |
|
214 | - } |
|
215 | - |
|
216 | - // We declare our instance of the Symfony command as a Mouf instance |
|
217 | - $generateCommand = InstallUtils::getOrCreateInstance('generateCommand', GenerateCommand::class, $this->moufManager); |
|
218 | - $generateCommand->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration); |
|
219 | - |
|
220 | - // We register that instance descriptor using "ConsoleUtils" |
|
221 | - $consoleUtils = new ConsoleUtils($this->moufManager); |
|
222 | - $consoleUtils->registerCommand($generateCommand); |
|
223 | - |
|
224 | - $this->moufManager->rewriteMouf(); |
|
225 | - |
|
226 | - TdbmController::generateDaos($this->moufManager, 'tdbmService', $daonamespace, $beannamespace, 'daoFactory', $selfedit, /*$storeInUtc,*/ $defaultPath, $storePath); |
|
227 | - |
|
228 | - InstallUtils::continueInstall($selfedit == 'true'); |
|
229 | - } |
|
230 | - |
|
231 | - protected $errorMsg; |
|
232 | - |
|
233 | - private function displayErrorMsg($msg) |
|
234 | - { |
|
235 | - $this->errorMsg = $msg; |
|
236 | - $this->content->addFile(__DIR__.'/../../../../views/installError.php', $this); |
|
237 | - $this->template->toHtml(); |
|
238 | - } |
|
24 | + /** |
|
25 | + * @var HtmlBlock |
|
26 | + */ |
|
27 | + public $content; |
|
28 | + |
|
29 | + public $selfedit; |
|
30 | + |
|
31 | + /** |
|
32 | + * The active MoufManager to be edited/viewed. |
|
33 | + * |
|
34 | + * @var MoufManager |
|
35 | + */ |
|
36 | + public $moufManager; |
|
37 | + |
|
38 | + /** |
|
39 | + * The template used by the main page for mouf. |
|
40 | + * |
|
41 | + * @Property |
|
42 | + * @Compulsory |
|
43 | + * |
|
44 | + * @var TemplateInterface |
|
45 | + */ |
|
46 | + public $template; |
|
47 | + |
|
48 | + /** |
|
49 | + * Displays the first install screen. |
|
50 | + * |
|
51 | + * @Action |
|
52 | + * @Logged |
|
53 | + * |
|
54 | + * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
55 | + */ |
|
56 | + public function defaultAction($selfedit = 'false') |
|
57 | + { |
|
58 | + $this->selfedit = $selfedit; |
|
59 | + |
|
60 | + if ($selfedit == 'true') { |
|
61 | + $this->moufManager = MoufManager::getMoufManager(); |
|
62 | + } else { |
|
63 | + $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
64 | + } |
|
65 | + |
|
66 | + $this->content->addFile(dirname(__FILE__).'/../../../../views/installStep1.php', $this); |
|
67 | + $this->template->toHtml(); |
|
68 | + } |
|
69 | + |
|
70 | + /** |
|
71 | + * Skips the install process. |
|
72 | + * |
|
73 | + * @Action |
|
74 | + * @Logged |
|
75 | + * |
|
76 | + * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
77 | + */ |
|
78 | + public function skip($selfedit = 'false') |
|
79 | + { |
|
80 | + InstallUtils::continueInstall($selfedit == 'true'); |
|
81 | + } |
|
82 | + |
|
83 | + protected $daoNamespace; |
|
84 | + protected $beanNamespace; |
|
85 | + protected $autoloadDetected; |
|
86 | + //protected $storeInUtc; |
|
87 | + protected $useCustomComposer = false; |
|
88 | + protected $composerFile; |
|
89 | + |
|
90 | + /** |
|
91 | + * Displays the second install screen. |
|
92 | + * |
|
93 | + * @Action |
|
94 | + * @Logged |
|
95 | + * |
|
96 | + * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only) |
|
97 | + */ |
|
98 | + public function configure($selfedit = 'false') |
|
99 | + { |
|
100 | + $this->selfedit = $selfedit; |
|
101 | + |
|
102 | + if ($selfedit == 'true') { |
|
103 | + $this->moufManager = MoufManager::getMoufManager(); |
|
104 | + } else { |
|
105 | + $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
106 | + } |
|
107 | + |
|
108 | + // Let's start by performing basic checks about the instances we assume to exist. |
|
109 | + if (!$this->moufManager->instanceExists('dbalConnection')) { |
|
110 | + $this->displayErrorMsg("The TDBM install process assumes your database connection instance is already created, and that the name of this instance is 'dbalConnection'. Could not find the 'dbalConnection' instance."); |
|
111 | + |
|
112 | + return; |
|
113 | + } |
|
114 | + |
|
115 | + if ($this->moufManager->has('tdbmConfiguration')) { |
|
116 | + $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration'); |
|
117 | + |
|
118 | + $this->beanNamespace = $tdbmConfiguration->getConstructorArgumentProperty('beanNamespace')->getValue(); |
|
119 | + $this->daoNamespace = $tdbmConfiguration->getConstructorArgumentProperty('daoNamespace')->getValue(); |
|
120 | + } else { |
|
121 | + // Old TDBM 4.2 fallback |
|
122 | + $this->daoNamespace = $this->moufManager->getVariable('tdbmDefaultDaoNamespace_tdbmService'); |
|
123 | + $this->beanNamespace = $this->moufManager->getVariable('tdbmDefaultBeanNamespace_tdbmService'); |
|
124 | + } |
|
125 | + |
|
126 | + if ($this->daoNamespace == null && $this->beanNamespace == null) { |
|
127 | + $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json'); |
|
128 | + |
|
129 | + $autoloadNamespaces = $classNameMapper->getManagedNamespaces(); |
|
130 | + if ($autoloadNamespaces) { |
|
131 | + $this->autoloadDetected = true; |
|
132 | + $rootNamespace = $autoloadNamespaces[0]; |
|
133 | + $this->daoNamespace = $rootNamespace.'Dao'; |
|
134 | + $this->beanNamespace = $rootNamespace.'Model'; |
|
135 | + } else { |
|
136 | + $this->autoloadDetected = false; |
|
137 | + $this->daoNamespace = 'YourApplication\\Dao'; |
|
138 | + $this->beanNamespace = 'YourApplication\\Model'; |
|
139 | + } |
|
140 | + } else { |
|
141 | + $this->autoloadDetected = true; |
|
142 | + } |
|
143 | + $this->defaultPath = true; |
|
144 | + $this->storePath = ''; |
|
145 | + |
|
146 | + $this->castDatesToDateTime = true; |
|
147 | + |
|
148 | + $this->content->addFile(__DIR__.'/../../../../views/installStep2.php', $this); |
|
149 | + $this->template->toHtml(); |
|
150 | + } |
|
151 | + |
|
152 | + /** |
|
153 | + * This action generates the TDBM instance, then the DAOs and Beans. |
|
154 | + * |
|
155 | + * @Action |
|
156 | + * |
|
157 | + * @param string $daonamespace |
|
158 | + * @param string $beannamespace |
|
159 | + * @param string $selfedit |
|
160 | + * |
|
161 | + * @throws \Mouf\MoufException |
|
162 | + */ |
|
163 | + public function generate($daonamespace, $beannamespace, /*$storeInUtc = 0,*/ $selfedit = 'false', $defaultPath = false, $storePath = '') |
|
164 | + { |
|
165 | + $this->selfedit = $selfedit; |
|
166 | + |
|
167 | + if ($selfedit == 'true') { |
|
168 | + $this->moufManager = MoufManager::getMoufManager(); |
|
169 | + } else { |
|
170 | + $this->moufManager = MoufManager::getMoufManagerHiddenInstance(); |
|
171 | + } |
|
172 | + |
|
173 | + $doctrineCache = $this->moufManager->getInstanceDescriptor('defaultDoctrineCache'); |
|
174 | + |
|
175 | + $migratingFrom42 = false; |
|
176 | + if ($this->moufManager->has('tdbmService') && !$this->moufManager->has('tdbmConfiguration')) { |
|
177 | + $migratingFrom42 = true; |
|
178 | + } |
|
179 | + |
|
180 | + $namingStrategy = InstallUtils::getOrCreateInstance('namingStrategy', DefaultNamingStrategy::class, $this->moufManager); |
|
181 | + if ($migratingFrom42) { |
|
182 | + // Let's setup the naming strategy for compatibility |
|
183 | + $namingStrategy->getSetterProperty('setBeanPrefix')->setValue(''); |
|
184 | + $namingStrategy->getSetterProperty('setBeanSuffix')->setValue('Bean'); |
|
185 | + $namingStrategy->getSetterProperty('setBaseBeanPrefix')->setValue(''); |
|
186 | + $namingStrategy->getSetterProperty('setBaseBeanSuffix')->setValue('BaseBean'); |
|
187 | + $namingStrategy->getSetterProperty('setDaoPrefix')->setValue(''); |
|
188 | + $namingStrategy->getSetterProperty('setDaoSuffix')->setValue('Dao'); |
|
189 | + $namingStrategy->getSetterProperty('setBaseDaoPrefix')->setValue(''); |
|
190 | + $namingStrategy->getSetterProperty('setBaseDaoSuffix')->setValue('BaseDao'); |
|
191 | + } |
|
192 | + |
|
193 | + if (!$this->moufManager->instanceExists('tdbmConfiguration')) { |
|
194 | + $moufListener = InstallUtils::getOrCreateInstance(MoufDiListener::class, MoufDiListener::class, $this->moufManager); |
|
195 | + |
|
196 | + $tdbmConfiguration = $this->moufManager->createInstance(MoufConfiguration::class)->setName('tdbmConfiguration'); |
|
197 | + $tdbmConfiguration->getConstructorArgumentProperty('connection')->setValue($this->moufManager->getInstanceDescriptor('dbalConnection')); |
|
198 | + $tdbmConfiguration->getConstructorArgumentProperty('cache')->setValue($doctrineCache); |
|
199 | + $tdbmConfiguration->getConstructorArgumentProperty('namingStrategy')->setValue($namingStrategy); |
|
200 | + $tdbmConfiguration->getProperty('daoFactoryInstanceName')->setValue('daoFactory'); |
|
201 | + $tdbmConfiguration->getConstructorArgumentProperty('generatorListeners')->setValue([$moufListener]); |
|
202 | + |
|
203 | + // Let's also delete the tdbmService if migrating versions <= 4.2 |
|
204 | + if ($migratingFrom42) { |
|
205 | + $this->moufManager->removeComponent('tdbmService'); |
|
206 | + } |
|
207 | + } else { |
|
208 | + $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration'); |
|
209 | + } |
|
210 | + |
|
211 | + if (!$this->moufManager->instanceExists('tdbmService')) { |
|
212 | + $tdbmService = $this->moufManager->createInstance('Mouf\\Database\\TDBM\\TDBMService')->setName('tdbmService'); |
|
213 | + $tdbmService->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration); |
|
214 | + } |
|
215 | + |
|
216 | + // We declare our instance of the Symfony command as a Mouf instance |
|
217 | + $generateCommand = InstallUtils::getOrCreateInstance('generateCommand', GenerateCommand::class, $this->moufManager); |
|
218 | + $generateCommand->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration); |
|
219 | + |
|
220 | + // We register that instance descriptor using "ConsoleUtils" |
|
221 | + $consoleUtils = new ConsoleUtils($this->moufManager); |
|
222 | + $consoleUtils->registerCommand($generateCommand); |
|
223 | + |
|
224 | + $this->moufManager->rewriteMouf(); |
|
225 | + |
|
226 | + TdbmController::generateDaos($this->moufManager, 'tdbmService', $daonamespace, $beannamespace, 'daoFactory', $selfedit, /*$storeInUtc,*/ $defaultPath, $storePath); |
|
227 | + |
|
228 | + InstallUtils::continueInstall($selfedit == 'true'); |
|
229 | + } |
|
230 | + |
|
231 | + protected $errorMsg; |
|
232 | + |
|
233 | + private function displayErrorMsg($msg) |
|
234 | + { |
|
235 | + $this->errorMsg = $msg; |
|
236 | + $this->content->addFile(__DIR__.'/../../../../views/installError.php', $this); |
|
237 | + $this->template->toHtml(); |
|
238 | + } |
|
239 | 239 | } |
@@ -17,132 +17,132 @@ discard block |
||
17 | 17 | */ |
18 | 18 | class TDBMDaoGenerator |
19 | 19 | { |
20 | - /** |
|
21 | - * @var Schema |
|
22 | - */ |
|
23 | - private $schema; |
|
24 | - |
|
25 | - /** |
|
26 | - * Name of composer file. |
|
27 | - * |
|
28 | - * @var string |
|
29 | - */ |
|
30 | - private $composerFile; |
|
31 | - |
|
32 | - /** |
|
33 | - * @var TDBMSchemaAnalyzer |
|
34 | - */ |
|
35 | - private $tdbmSchemaAnalyzer; |
|
36 | - |
|
37 | - /** |
|
38 | - * @var GeneratorListenerInterface |
|
39 | - */ |
|
40 | - private $eventDispatcher; |
|
41 | - |
|
42 | - /** |
|
43 | - * @var NamingStrategyInterface |
|
44 | - */ |
|
45 | - private $namingStrategy; |
|
46 | - /** |
|
47 | - * @var ConfigurationInterface |
|
48 | - */ |
|
49 | - private $configuration; |
|
50 | - |
|
51 | - /** |
|
52 | - * Constructor. |
|
53 | - * |
|
54 | - * @param ConfigurationInterface $configuration |
|
55 | - * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer |
|
56 | - */ |
|
57 | - public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer) |
|
58 | - { |
|
59 | - $this->configuration = $configuration; |
|
60 | - $this->schema = $tdbmSchemaAnalyzer->getSchema(); |
|
61 | - $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer; |
|
62 | - $this->namingStrategy = $configuration->getNamingStrategy(); |
|
63 | - $this->eventDispatcher = $configuration->getGeneratorEventDispatcher(); |
|
64 | - } |
|
65 | - |
|
66 | - /** |
|
67 | - * Generates all the daos and beans. |
|
68 | - * |
|
69 | - * @throws TDBMException |
|
70 | - */ |
|
71 | - public function generateAllDaosAndBeans(): void |
|
72 | - { |
|
73 | - // TODO: check that no class name ends with "Base". Otherwise, there will be name clash. |
|
74 | - |
|
75 | - $tableList = $this->schema->getTables(); |
|
76 | - |
|
77 | - // Remove all beans and daos from junction tables |
|
78 | - $junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true); |
|
79 | - $junctionTableNames = array_map(function (Table $table) { |
|
80 | - return $table->getName(); |
|
81 | - }, $junctionTables); |
|
82 | - |
|
83 | - $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) { |
|
84 | - return !in_array($table->getName(), $junctionTableNames); |
|
85 | - }); |
|
86 | - |
|
87 | - $beanDescriptors = []; |
|
88 | - |
|
89 | - foreach ($tableList as $table) { |
|
90 | - $beanDescriptors[] = $this->generateDaoAndBean($table); |
|
91 | - } |
|
92 | - |
|
93 | - |
|
94 | - $this->generateFactory($tableList); |
|
95 | - |
|
96 | - // Let's call the list of listeners |
|
97 | - $this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors); |
|
98 | - } |
|
99 | - |
|
100 | - /** |
|
101 | - * Generates in one method call the daos and the beans for one table. |
|
102 | - * |
|
103 | - * @param Table $table |
|
104 | - * |
|
105 | - * @return BeanDescriptor |
|
106 | - * @throws TDBMException |
|
107 | - */ |
|
108 | - private function generateDaoAndBean(Table $table) : BeanDescriptor |
|
109 | - { |
|
110 | - $tableName = $table->getName(); |
|
111 | - $daoName = $this->namingStrategy->getDaoClassName($tableName); |
|
112 | - $beanName = $this->namingStrategy->getBeanClassName($tableName); |
|
113 | - $baseBeanName = $this->namingStrategy->getBaseBeanClassName($tableName); |
|
114 | - $baseDaoName = $this->namingStrategy->getBaseDaoClassName($tableName); |
|
115 | - |
|
116 | - $beanDescriptor = new BeanDescriptor($table, $this->configuration->getSchemaAnalyzer(), $this->schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy); |
|
117 | - $this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table); |
|
118 | - $this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table); |
|
119 | - return $beanDescriptor; |
|
120 | - } |
|
121 | - |
|
122 | - /** |
|
123 | - * Writes the PHP bean file with all getters and setters from the table passed in parameter. |
|
124 | - * |
|
125 | - * @param BeanDescriptor $beanDescriptor |
|
126 | - * @param string $className The name of the class |
|
127 | - * @param string $baseClassName The name of the base class which will be extended (name only, no directory) |
|
128 | - * @param Table $table The table |
|
129 | - * |
|
130 | - * @throws TDBMException |
|
131 | - */ |
|
132 | - public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table) |
|
133 | - { |
|
134 | - $beannamespace = $this->configuration->getBeanNamespace(); |
|
135 | - $str = $beanDescriptor->generatePhpCode($beannamespace); |
|
136 | - |
|
137 | - $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
|
138 | - |
|
139 | - $this->dumpFile($possibleBaseFileName, $str); |
|
140 | - |
|
141 | - $possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname(); |
|
142 | - |
|
143 | - if (!file_exists($possibleFileName)) { |
|
144 | - $tableName = $table->getName(); |
|
145 | - $str = "<?php |
|
20 | + /** |
|
21 | + * @var Schema |
|
22 | + */ |
|
23 | + private $schema; |
|
24 | + |
|
25 | + /** |
|
26 | + * Name of composer file. |
|
27 | + * |
|
28 | + * @var string |
|
29 | + */ |
|
30 | + private $composerFile; |
|
31 | + |
|
32 | + /** |
|
33 | + * @var TDBMSchemaAnalyzer |
|
34 | + */ |
|
35 | + private $tdbmSchemaAnalyzer; |
|
36 | + |
|
37 | + /** |
|
38 | + * @var GeneratorListenerInterface |
|
39 | + */ |
|
40 | + private $eventDispatcher; |
|
41 | + |
|
42 | + /** |
|
43 | + * @var NamingStrategyInterface |
|
44 | + */ |
|
45 | + private $namingStrategy; |
|
46 | + /** |
|
47 | + * @var ConfigurationInterface |
|
48 | + */ |
|
49 | + private $configuration; |
|
50 | + |
|
51 | + /** |
|
52 | + * Constructor. |
|
53 | + * |
|
54 | + * @param ConfigurationInterface $configuration |
|
55 | + * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer |
|
56 | + */ |
|
57 | + public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer) |
|
58 | + { |
|
59 | + $this->configuration = $configuration; |
|
60 | + $this->schema = $tdbmSchemaAnalyzer->getSchema(); |
|
61 | + $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer; |
|
62 | + $this->namingStrategy = $configuration->getNamingStrategy(); |
|
63 | + $this->eventDispatcher = $configuration->getGeneratorEventDispatcher(); |
|
64 | + } |
|
65 | + |
|
66 | + /** |
|
67 | + * Generates all the daos and beans. |
|
68 | + * |
|
69 | + * @throws TDBMException |
|
70 | + */ |
|
71 | + public function generateAllDaosAndBeans(): void |
|
72 | + { |
|
73 | + // TODO: check that no class name ends with "Base". Otherwise, there will be name clash. |
|
74 | + |
|
75 | + $tableList = $this->schema->getTables(); |
|
76 | + |
|
77 | + // Remove all beans and daos from junction tables |
|
78 | + $junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true); |
|
79 | + $junctionTableNames = array_map(function (Table $table) { |
|
80 | + return $table->getName(); |
|
81 | + }, $junctionTables); |
|
82 | + |
|
83 | + $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) { |
|
84 | + return !in_array($table->getName(), $junctionTableNames); |
|
85 | + }); |
|
86 | + |
|
87 | + $beanDescriptors = []; |
|
88 | + |
|
89 | + foreach ($tableList as $table) { |
|
90 | + $beanDescriptors[] = $this->generateDaoAndBean($table); |
|
91 | + } |
|
92 | + |
|
93 | + |
|
94 | + $this->generateFactory($tableList); |
|
95 | + |
|
96 | + // Let's call the list of listeners |
|
97 | + $this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors); |
|
98 | + } |
|
99 | + |
|
100 | + /** |
|
101 | + * Generates in one method call the daos and the beans for one table. |
|
102 | + * |
|
103 | + * @param Table $table |
|
104 | + * |
|
105 | + * @return BeanDescriptor |
|
106 | + * @throws TDBMException |
|
107 | + */ |
|
108 | + private function generateDaoAndBean(Table $table) : BeanDescriptor |
|
109 | + { |
|
110 | + $tableName = $table->getName(); |
|
111 | + $daoName = $this->namingStrategy->getDaoClassName($tableName); |
|
112 | + $beanName = $this->namingStrategy->getBeanClassName($tableName); |
|
113 | + $baseBeanName = $this->namingStrategy->getBaseBeanClassName($tableName); |
|
114 | + $baseDaoName = $this->namingStrategy->getBaseDaoClassName($tableName); |
|
115 | + |
|
116 | + $beanDescriptor = new BeanDescriptor($table, $this->configuration->getSchemaAnalyzer(), $this->schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy); |
|
117 | + $this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table); |
|
118 | + $this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table); |
|
119 | + return $beanDescriptor; |
|
120 | + } |
|
121 | + |
|
122 | + /** |
|
123 | + * Writes the PHP bean file with all getters and setters from the table passed in parameter. |
|
124 | + * |
|
125 | + * @param BeanDescriptor $beanDescriptor |
|
126 | + * @param string $className The name of the class |
|
127 | + * @param string $baseClassName The name of the base class which will be extended (name only, no directory) |
|
128 | + * @param Table $table The table |
|
129 | + * |
|
130 | + * @throws TDBMException |
|
131 | + */ |
|
132 | + public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table) |
|
133 | + { |
|
134 | + $beannamespace = $this->configuration->getBeanNamespace(); |
|
135 | + $str = $beanDescriptor->generatePhpCode($beannamespace); |
|
136 | + |
|
137 | + $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
|
138 | + |
|
139 | + $this->dumpFile($possibleBaseFileName, $str); |
|
140 | + |
|
141 | + $possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname(); |
|
142 | + |
|
143 | + if (!file_exists($possibleFileName)) { |
|
144 | + $tableName = $table->getName(); |
|
145 | + $str = "<?php |
|
146 | 146 | /* |
147 | 147 | * This file has been automatically generated by TDBM. |
148 | 148 | * You can edit this file as it will not be overwritten. |
@@ -160,73 +160,73 @@ discard block |
||
160 | 160 | } |
161 | 161 | "; |
162 | 162 | |
163 | - $this->dumpFile($possibleFileName, $str); |
|
164 | - } |
|
165 | - } |
|
166 | - |
|
167 | - /** |
|
168 | - * Tries to find a @defaultSort annotation in one of the columns. |
|
169 | - * |
|
170 | - * @param Table $table |
|
171 | - * |
|
172 | - * @return array First item: column name, Second item: column order (asc/desc) |
|
173 | - */ |
|
174 | - private function getDefaultSortColumnFromAnnotation(Table $table) |
|
175 | - { |
|
176 | - $defaultSort = null; |
|
177 | - $defaultSortDirection = null; |
|
178 | - foreach ($table->getColumns() as $column) { |
|
179 | - $comments = $column->getComment(); |
|
180 | - $matches = []; |
|
181 | - if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) { |
|
182 | - $defaultSort = $column->getName(); |
|
183 | - if (count($matches) === 3) { |
|
184 | - $defaultSortDirection = $matches[2]; |
|
185 | - } else { |
|
186 | - $defaultSortDirection = 'ASC'; |
|
187 | - } |
|
188 | - } |
|
189 | - } |
|
190 | - |
|
191 | - return [$defaultSort, $defaultSortDirection]; |
|
192 | - } |
|
193 | - |
|
194 | - /** |
|
195 | - * Writes the PHP bean DAO with simple functions to create/get/save objects. |
|
196 | - * |
|
197 | - * @param BeanDescriptor $beanDescriptor |
|
198 | - * @param string $className The name of the class |
|
199 | - * @param string $baseClassName |
|
200 | - * @param string $beanClassName |
|
201 | - * @param Table $table |
|
202 | - * |
|
203 | - * @throws TDBMException |
|
204 | - */ |
|
205 | - private function generateDao(BeanDescriptor $beanDescriptor, string $className, string $baseClassName, string $beanClassName, Table $table) |
|
206 | - { |
|
207 | - $daonamespace = $this->configuration->getDaoNamespace(); |
|
208 | - $beannamespace = $this->configuration->getBeanNamespace(); |
|
209 | - $tableName = $table->getName(); |
|
210 | - $primaryKeyColumns = $table->getPrimaryKeyColumns(); |
|
211 | - |
|
212 | - list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table); |
|
213 | - |
|
214 | - // FIXME: lowercase tables with _ in the name should work! |
|
215 | - $tableCamel = self::toSingular(self::toCamelCase($tableName)); |
|
216 | - |
|
217 | - $beanClassWithoutNameSpace = $beanClassName; |
|
218 | - $beanClassName = $beannamespace.'\\'.$beanClassName; |
|
219 | - |
|
220 | - list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace); |
|
221 | - |
|
222 | - $usedBeans[] = $beanClassName; |
|
223 | - // Let's suppress duplicates in used beans (if any) |
|
224 | - $usedBeans = array_flip(array_flip($usedBeans)); |
|
225 | - $useStatements = array_map(function ($usedBean) { |
|
226 | - return "use $usedBean;\n"; |
|
227 | - }, $usedBeans); |
|
228 | - |
|
229 | - $str = "<?php |
|
163 | + $this->dumpFile($possibleFileName, $str); |
|
164 | + } |
|
165 | + } |
|
166 | + |
|
167 | + /** |
|
168 | + * Tries to find a @defaultSort annotation in one of the columns. |
|
169 | + * |
|
170 | + * @param Table $table |
|
171 | + * |
|
172 | + * @return array First item: column name, Second item: column order (asc/desc) |
|
173 | + */ |
|
174 | + private function getDefaultSortColumnFromAnnotation(Table $table) |
|
175 | + { |
|
176 | + $defaultSort = null; |
|
177 | + $defaultSortDirection = null; |
|
178 | + foreach ($table->getColumns() as $column) { |
|
179 | + $comments = $column->getComment(); |
|
180 | + $matches = []; |
|
181 | + if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) { |
|
182 | + $defaultSort = $column->getName(); |
|
183 | + if (count($matches) === 3) { |
|
184 | + $defaultSortDirection = $matches[2]; |
|
185 | + } else { |
|
186 | + $defaultSortDirection = 'ASC'; |
|
187 | + } |
|
188 | + } |
|
189 | + } |
|
190 | + |
|
191 | + return [$defaultSort, $defaultSortDirection]; |
|
192 | + } |
|
193 | + |
|
194 | + /** |
|
195 | + * Writes the PHP bean DAO with simple functions to create/get/save objects. |
|
196 | + * |
|
197 | + * @param BeanDescriptor $beanDescriptor |
|
198 | + * @param string $className The name of the class |
|
199 | + * @param string $baseClassName |
|
200 | + * @param string $beanClassName |
|
201 | + * @param Table $table |
|
202 | + * |
|
203 | + * @throws TDBMException |
|
204 | + */ |
|
205 | + private function generateDao(BeanDescriptor $beanDescriptor, string $className, string $baseClassName, string $beanClassName, Table $table) |
|
206 | + { |
|
207 | + $daonamespace = $this->configuration->getDaoNamespace(); |
|
208 | + $beannamespace = $this->configuration->getBeanNamespace(); |
|
209 | + $tableName = $table->getName(); |
|
210 | + $primaryKeyColumns = $table->getPrimaryKeyColumns(); |
|
211 | + |
|
212 | + list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table); |
|
213 | + |
|
214 | + // FIXME: lowercase tables with _ in the name should work! |
|
215 | + $tableCamel = self::toSingular(self::toCamelCase($tableName)); |
|
216 | + |
|
217 | + $beanClassWithoutNameSpace = $beanClassName; |
|
218 | + $beanClassName = $beannamespace.'\\'.$beanClassName; |
|
219 | + |
|
220 | + list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace); |
|
221 | + |
|
222 | + $usedBeans[] = $beanClassName; |
|
223 | + // Let's suppress duplicates in used beans (if any) |
|
224 | + $usedBeans = array_flip(array_flip($usedBeans)); |
|
225 | + $useStatements = array_map(function ($usedBean) { |
|
226 | + return "use $usedBean;\n"; |
|
227 | + }, $usedBeans); |
|
228 | + |
|
229 | + $str = "<?php |
|
230 | 230 | |
231 | 231 | /* |
232 | 232 | * This file has been automatically generated by TDBM. |
@@ -302,10 +302,10 @@ discard block |
||
302 | 302 | } |
303 | 303 | "; |
304 | 304 | |
305 | - if (count($primaryKeyColumns) === 1) { |
|
306 | - $primaryKeyColumn = $primaryKeyColumns[0]; |
|
307 | - $primaryKeyPhpType = self::dbalTypeToPhpType($table->getColumn($primaryKeyColumn)->getType()); |
|
308 | - $str .= " |
|
305 | + if (count($primaryKeyColumns) === 1) { |
|
306 | + $primaryKeyColumn = $primaryKeyColumns[0]; |
|
307 | + $primaryKeyPhpType = self::dbalTypeToPhpType($table->getColumn($primaryKeyColumn)->getType()); |
|
308 | + $str .= " |
|
309 | 309 | /** |
310 | 310 | * Get $beanClassWithoutNameSpace specified by its ID (its primary key) |
311 | 311 | * If the primary key does not exist, an exception is thrown. |
@@ -320,8 +320,8 @@ discard block |
||
320 | 320 | return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading); |
321 | 321 | } |
322 | 322 | "; |
323 | - } |
|
324 | - $str .= " |
|
323 | + } |
|
324 | + $str .= " |
|
325 | 325 | /** |
326 | 326 | * Deletes the $beanClassWithoutNameSpace passed in parameter. |
327 | 327 | * |
@@ -421,19 +421,19 @@ discard block |
||
421 | 421 | } |
422 | 422 | "; |
423 | 423 | |
424 | - $str .= $findByDaoCode; |
|
425 | - $str .= '} |
|
424 | + $str .= $findByDaoCode; |
|
425 | + $str .= '} |
|
426 | 426 | '; |
427 | 427 | |
428 | - $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
|
428 | + $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
|
429 | 429 | |
430 | - $this->dumpFile($possibleBaseFileName, $str); |
|
430 | + $this->dumpFile($possibleBaseFileName, $str); |
|
431 | 431 | |
432 | - $possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname(); |
|
432 | + $possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname(); |
|
433 | 433 | |
434 | - // Now, let's generate the "editable" class |
|
435 | - if (!file_exists($possibleFileName)) { |
|
436 | - $str = "<?php |
|
434 | + // Now, let's generate the "editable" class |
|
435 | + if (!file_exists($possibleFileName)) { |
|
436 | + $str = "<?php |
|
437 | 437 | |
438 | 438 | /* |
439 | 439 | * This file has been automatically generated by TDBM. |
@@ -451,24 +451,24 @@ discard block |
||
451 | 451 | { |
452 | 452 | } |
453 | 453 | "; |
454 | - $this->dumpFile($possibleFileName, $str); |
|
455 | - } |
|
456 | - } |
|
454 | + $this->dumpFile($possibleFileName, $str); |
|
455 | + } |
|
456 | + } |
|
457 | 457 | |
458 | - /** |
|
459 | - * Generates the factory bean. |
|
460 | - * |
|
461 | - * @param Table[] $tableList |
|
462 | - * @throws TDBMException |
|
463 | - */ |
|
464 | - private function generateFactory(array $tableList) : void |
|
465 | - { |
|
466 | - $daoNamespace = $this->configuration->getDaoNamespace(); |
|
467 | - $daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName(); |
|
458 | + /** |
|
459 | + * Generates the factory bean. |
|
460 | + * |
|
461 | + * @param Table[] $tableList |
|
462 | + * @throws TDBMException |
|
463 | + */ |
|
464 | + private function generateFactory(array $tableList) : void |
|
465 | + { |
|
466 | + $daoNamespace = $this->configuration->getDaoNamespace(); |
|
467 | + $daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName(); |
|
468 | 468 | |
469 | - // For each table, let's write a property. |
|
469 | + // For each table, let's write a property. |
|
470 | 470 | |
471 | - $str = "<?php |
|
471 | + $str = "<?php |
|
472 | 472 | |
473 | 473 | /* |
474 | 474 | * This file has been automatically generated by TDBM. |
@@ -478,13 +478,13 @@ discard block |
||
478 | 478 | namespace {$daoNamespace}\\Generated; |
479 | 479 | |
480 | 480 | "; |
481 | - foreach ($tableList as $table) { |
|
482 | - $tableName = $table->getName(); |
|
483 | - $daoClassName = $this->namingStrategy->getDaoClassName($tableName); |
|
484 | - $str .= "use {$daoNamespace}\\".$daoClassName.";\n"; |
|
485 | - } |
|
481 | + foreach ($tableList as $table) { |
|
482 | + $tableName = $table->getName(); |
|
483 | + $daoClassName = $this->namingStrategy->getDaoClassName($tableName); |
|
484 | + $str .= "use {$daoNamespace}\\".$daoClassName.";\n"; |
|
485 | + } |
|
486 | 486 | |
487 | - $str .= " |
|
487 | + $str .= " |
|
488 | 488 | /** |
489 | 489 | * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM. |
490 | 490 | * |
@@ -493,12 +493,12 @@ discard block |
||
493 | 493 | { |
494 | 494 | "; |
495 | 495 | |
496 | - foreach ($tableList as $table) { |
|
497 | - $tableName = $table->getName(); |
|
498 | - $daoClassName = $this->namingStrategy->getDaoClassName($tableName); |
|
499 | - $daoInstanceName = self::toVariableName($daoClassName); |
|
496 | + foreach ($tableList as $table) { |
|
497 | + $tableName = $table->getName(); |
|
498 | + $daoClassName = $this->namingStrategy->getDaoClassName($tableName); |
|
499 | + $daoInstanceName = self::toVariableName($daoClassName); |
|
500 | 500 | |
501 | - $str .= ' /** |
|
501 | + $str .= ' /** |
|
502 | 502 | * @var '.$daoClassName.' |
503 | 503 | */ |
504 | 504 | private $'.$daoInstanceName.'; |
@@ -522,131 +522,131 @@ discard block |
||
522 | 522 | { |
523 | 523 | $this->'.$daoInstanceName.' = $'.$daoInstanceName.'; |
524 | 524 | }'; |
525 | - } |
|
525 | + } |
|
526 | 526 | |
527 | - $str .= ' |
|
527 | + $str .= ' |
|
528 | 528 | } |
529 | 529 | '; |
530 | 530 | |
531 | - $possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname(); |
|
532 | - |
|
533 | - $this->dumpFile($possibleFileName, $str); |
|
534 | - } |
|
535 | - |
|
536 | - /** |
|
537 | - * Transforms a string to camelCase (except the first letter will be uppercase too). |
|
538 | - * Underscores and spaces are removed and the first letter after the underscore is uppercased. |
|
539 | - * |
|
540 | - * @param $str string |
|
541 | - * |
|
542 | - * @return string |
|
543 | - */ |
|
544 | - public static function toCamelCase($str) |
|
545 | - { |
|
546 | - $str = strtoupper(substr($str, 0, 1)).substr($str, 1); |
|
547 | - while (true) { |
|
548 | - if (strpos($str, '_') === false && strpos($str, ' ') === false) { |
|
549 | - break; |
|
550 | - } |
|
551 | - |
|
552 | - $pos = strpos($str, '_'); |
|
553 | - if ($pos === false) { |
|
554 | - $pos = strpos($str, ' '); |
|
555 | - } |
|
556 | - $before = substr($str, 0, $pos); |
|
557 | - $after = substr($str, $pos + 1); |
|
558 | - $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1); |
|
559 | - } |
|
560 | - |
|
561 | - return $str; |
|
562 | - } |
|
563 | - |
|
564 | - /** |
|
565 | - * Tries to put string to the singular form (if it is plural). |
|
566 | - * We assume the table names are in english. |
|
567 | - * |
|
568 | - * @param $str string |
|
569 | - * |
|
570 | - * @return string |
|
571 | - */ |
|
572 | - public static function toSingular($str) |
|
573 | - { |
|
574 | - return Inflector::singularize($str); |
|
575 | - } |
|
576 | - |
|
577 | - /** |
|
578 | - * Put the first letter of the string in lower case. |
|
579 | - * Very useful to transform a class name into a variable name. |
|
580 | - * |
|
581 | - * @param $str string |
|
582 | - * |
|
583 | - * @return string |
|
584 | - */ |
|
585 | - public static function toVariableName($str) |
|
586 | - { |
|
587 | - return strtolower(substr($str, 0, 1)).substr($str, 1); |
|
588 | - } |
|
589 | - |
|
590 | - /** |
|
591 | - * Ensures the file passed in parameter can be written in its directory. |
|
592 | - * |
|
593 | - * @param string $fileName |
|
594 | - * |
|
595 | - * @throws TDBMException |
|
596 | - */ |
|
597 | - private function ensureDirectoryExist(string $fileName) |
|
598 | - { |
|
599 | - $dirName = dirname($fileName); |
|
600 | - if (!file_exists($dirName)) { |
|
601 | - $old = umask(0); |
|
602 | - $result = mkdir($dirName, 0775, true); |
|
603 | - umask($old); |
|
604 | - if ($result === false) { |
|
605 | - throw new TDBMException("Unable to create directory: '".$dirName."'."); |
|
606 | - } |
|
607 | - } |
|
608 | - } |
|
609 | - |
|
610 | - private function dumpFile(string $fileName, string $content) : void |
|
611 | - { |
|
612 | - $this->ensureDirectoryExist($fileName); |
|
613 | - $fileSystem = new Filesystem(); |
|
614 | - $fileSystem->dumpFile($fileName, $content); |
|
615 | - @chmod($fileName, 0664); |
|
616 | - } |
|
617 | - |
|
618 | - /** |
|
619 | - * Transforms a DBAL type into a PHP type (for PHPDoc purpose). |
|
620 | - * |
|
621 | - * @param Type $type The DBAL type |
|
622 | - * |
|
623 | - * @return string The PHP type |
|
624 | - */ |
|
625 | - public static function dbalTypeToPhpType(Type $type) |
|
626 | - { |
|
627 | - $map = [ |
|
628 | - Type::TARRAY => 'array', |
|
629 | - Type::SIMPLE_ARRAY => 'array', |
|
630 | - 'json' => 'array', // 'json' is supported from Doctrine DBAL 2.6 only. |
|
631 | - Type::JSON_ARRAY => 'array', |
|
632 | - Type::BIGINT => 'string', |
|
633 | - Type::BOOLEAN => 'bool', |
|
634 | - Type::DATETIME => '\DateTimeInterface', |
|
635 | - Type::DATETIMETZ => '\DateTimeInterface', |
|
636 | - Type::DATE => '\DateTimeInterface', |
|
637 | - Type::TIME => '\DateTimeInterface', |
|
638 | - Type::DECIMAL => 'float', |
|
639 | - Type::INTEGER => 'int', |
|
640 | - Type::OBJECT => 'string', |
|
641 | - Type::SMALLINT => 'int', |
|
642 | - Type::STRING => 'string', |
|
643 | - Type::TEXT => 'string', |
|
644 | - Type::BINARY => 'string', |
|
645 | - Type::BLOB => 'string', |
|
646 | - Type::FLOAT => 'float', |
|
647 | - Type::GUID => 'string', |
|
648 | - ]; |
|
649 | - |
|
650 | - return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName(); |
|
651 | - } |
|
531 | + $possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname(); |
|
532 | + |
|
533 | + $this->dumpFile($possibleFileName, $str); |
|
534 | + } |
|
535 | + |
|
536 | + /** |
|
537 | + * Transforms a string to camelCase (except the first letter will be uppercase too). |
|
538 | + * Underscores and spaces are removed and the first letter after the underscore is uppercased. |
|
539 | + * |
|
540 | + * @param $str string |
|
541 | + * |
|
542 | + * @return string |
|
543 | + */ |
|
544 | + public static function toCamelCase($str) |
|
545 | + { |
|
546 | + $str = strtoupper(substr($str, 0, 1)).substr($str, 1); |
|
547 | + while (true) { |
|
548 | + if (strpos($str, '_') === false && strpos($str, ' ') === false) { |
|
549 | + break; |
|
550 | + } |
|
551 | + |
|
552 | + $pos = strpos($str, '_'); |
|
553 | + if ($pos === false) { |
|
554 | + $pos = strpos($str, ' '); |
|
555 | + } |
|
556 | + $before = substr($str, 0, $pos); |
|
557 | + $after = substr($str, $pos + 1); |
|
558 | + $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1); |
|
559 | + } |
|
560 | + |
|
561 | + return $str; |
|
562 | + } |
|
563 | + |
|
564 | + /** |
|
565 | + * Tries to put string to the singular form (if it is plural). |
|
566 | + * We assume the table names are in english. |
|
567 | + * |
|
568 | + * @param $str string |
|
569 | + * |
|
570 | + * @return string |
|
571 | + */ |
|
572 | + public static function toSingular($str) |
|
573 | + { |
|
574 | + return Inflector::singularize($str); |
|
575 | + } |
|
576 | + |
|
577 | + /** |
|
578 | + * Put the first letter of the string in lower case. |
|
579 | + * Very useful to transform a class name into a variable name. |
|
580 | + * |
|
581 | + * @param $str string |
|
582 | + * |
|
583 | + * @return string |
|
584 | + */ |
|
585 | + public static function toVariableName($str) |
|
586 | + { |
|
587 | + return strtolower(substr($str, 0, 1)).substr($str, 1); |
|
588 | + } |
|
589 | + |
|
590 | + /** |
|
591 | + * Ensures the file passed in parameter can be written in its directory. |
|
592 | + * |
|
593 | + * @param string $fileName |
|
594 | + * |
|
595 | + * @throws TDBMException |
|
596 | + */ |
|
597 | + private function ensureDirectoryExist(string $fileName) |
|
598 | + { |
|
599 | + $dirName = dirname($fileName); |
|
600 | + if (!file_exists($dirName)) { |
|
601 | + $old = umask(0); |
|
602 | + $result = mkdir($dirName, 0775, true); |
|
603 | + umask($old); |
|
604 | + if ($result === false) { |
|
605 | + throw new TDBMException("Unable to create directory: '".$dirName."'."); |
|
606 | + } |
|
607 | + } |
|
608 | + } |
|
609 | + |
|
610 | + private function dumpFile(string $fileName, string $content) : void |
|
611 | + { |
|
612 | + $this->ensureDirectoryExist($fileName); |
|
613 | + $fileSystem = new Filesystem(); |
|
614 | + $fileSystem->dumpFile($fileName, $content); |
|
615 | + @chmod($fileName, 0664); |
|
616 | + } |
|
617 | + |
|
618 | + /** |
|
619 | + * Transforms a DBAL type into a PHP type (for PHPDoc purpose). |
|
620 | + * |
|
621 | + * @param Type $type The DBAL type |
|
622 | + * |
|
623 | + * @return string The PHP type |
|
624 | + */ |
|
625 | + public static function dbalTypeToPhpType(Type $type) |
|
626 | + { |
|
627 | + $map = [ |
|
628 | + Type::TARRAY => 'array', |
|
629 | + Type::SIMPLE_ARRAY => 'array', |
|
630 | + 'json' => 'array', // 'json' is supported from Doctrine DBAL 2.6 only. |
|
631 | + Type::JSON_ARRAY => 'array', |
|
632 | + Type::BIGINT => 'string', |
|
633 | + Type::BOOLEAN => 'bool', |
|
634 | + Type::DATETIME => '\DateTimeInterface', |
|
635 | + Type::DATETIMETZ => '\DateTimeInterface', |
|
636 | + Type::DATE => '\DateTimeInterface', |
|
637 | + Type::TIME => '\DateTimeInterface', |
|
638 | + Type::DECIMAL => 'float', |
|
639 | + Type::INTEGER => 'int', |
|
640 | + Type::OBJECT => 'string', |
|
641 | + Type::SMALLINT => 'int', |
|
642 | + Type::STRING => 'string', |
|
643 | + Type::TEXT => 'string', |
|
644 | + Type::BINARY => 'string', |
|
645 | + Type::BLOB => 'string', |
|
646 | + Type::FLOAT => 'float', |
|
647 | + Type::GUID => 'string', |
|
648 | + ]; |
|
649 | + |
|
650 | + return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName(); |
|
651 | + } |
|
652 | 652 | } |
@@ -5,58 +5,58 @@ |
||
5 | 5 | |
6 | 6 | class PathFinder implements PathFinderInterface |
7 | 7 | { |
8 | - /** |
|
9 | - * @var string |
|
10 | - */ |
|
11 | - private $composerFile; |
|
12 | - |
|
13 | - /** |
|
14 | - * @var string |
|
15 | - */ |
|
16 | - private $rootPath; |
|
17 | - |
|
18 | - /** |
|
19 | - * @var bool |
|
20 | - */ |
|
21 | - private $useAutoloadDev; |
|
22 | - |
|
23 | - /** |
|
24 | - * @var ClassNameMapper |
|
25 | - */ |
|
26 | - private $classNameMapper; |
|
27 | - |
|
28 | - public function __construct(string $composerFile = null, string $rootPath = null, bool $useAutoloadDev = false) |
|
29 | - { |
|
30 | - $this->composerFile = $composerFile; |
|
31 | - $this->useAutoloadDev = $useAutoloadDev; |
|
32 | - if ($rootPath === null) { |
|
33 | - $this->rootPath = dirname(__DIR__, 9); |
|
34 | - } else { |
|
35 | - $this->rootPath = $rootPath; |
|
36 | - } |
|
37 | - } |
|
38 | - |
|
39 | - private function getClassNameMapper() : ClassNameMapper |
|
40 | - { |
|
41 | - if ($this->classNameMapper === null) { |
|
42 | - $this->classNameMapper = ClassNameMapper::createFromComposerFile($this->composerFile, $this->rootPath, $this->useAutoloadDev); |
|
43 | - } |
|
44 | - return $this->classNameMapper; |
|
45 | - } |
|
46 | - |
|
47 | - /** |
|
48 | - * Returns the path of a class file given the fully qualified class name. |
|
49 | - * |
|
50 | - * @param string $className |
|
51 | - * @return \SplFileInfo |
|
52 | - * @throws NoPathFoundException |
|
53 | - */ |
|
54 | - public function getPath(string $className): \SplFileInfo |
|
55 | - { |
|
56 | - $paths = $this->getClassNameMapper()->getPossibleFileNames($className); |
|
57 | - if (empty($paths)) { |
|
58 | - throw NoPathFoundException::create($className); |
|
59 | - } |
|
60 | - return new \SplFileInfo($this->rootPath.'/'.$paths[0]); |
|
61 | - } |
|
8 | + /** |
|
9 | + * @var string |
|
10 | + */ |
|
11 | + private $composerFile; |
|
12 | + |
|
13 | + /** |
|
14 | + * @var string |
|
15 | + */ |
|
16 | + private $rootPath; |
|
17 | + |
|
18 | + /** |
|
19 | + * @var bool |
|
20 | + */ |
|
21 | + private $useAutoloadDev; |
|
22 | + |
|
23 | + /** |
|
24 | + * @var ClassNameMapper |
|
25 | + */ |
|
26 | + private $classNameMapper; |
|
27 | + |
|
28 | + public function __construct(string $composerFile = null, string $rootPath = null, bool $useAutoloadDev = false) |
|
29 | + { |
|
30 | + $this->composerFile = $composerFile; |
|
31 | + $this->useAutoloadDev = $useAutoloadDev; |
|
32 | + if ($rootPath === null) { |
|
33 | + $this->rootPath = dirname(__DIR__, 9); |
|
34 | + } else { |
|
35 | + $this->rootPath = $rootPath; |
|
36 | + } |
|
37 | + } |
|
38 | + |
|
39 | + private function getClassNameMapper() : ClassNameMapper |
|
40 | + { |
|
41 | + if ($this->classNameMapper === null) { |
|
42 | + $this->classNameMapper = ClassNameMapper::createFromComposerFile($this->composerFile, $this->rootPath, $this->useAutoloadDev); |
|
43 | + } |
|
44 | + return $this->classNameMapper; |
|
45 | + } |
|
46 | + |
|
47 | + /** |
|
48 | + * Returns the path of a class file given the fully qualified class name. |
|
49 | + * |
|
50 | + * @param string $className |
|
51 | + * @return \SplFileInfo |
|
52 | + * @throws NoPathFoundException |
|
53 | + */ |
|
54 | + public function getPath(string $className): \SplFileInfo |
|
55 | + { |
|
56 | + $paths = $this->getClassNameMapper()->getPossibleFileNames($className); |
|
57 | + if (empty($paths)) { |
|
58 | + throw NoPathFoundException::create($className); |
|
59 | + } |
|
60 | + return new \SplFileInfo($this->rootPath.'/'.$paths[0]); |
|
61 | + } |
|
62 | 62 | } |
@@ -7,193 +7,193 @@ |
||
7 | 7 | |
8 | 8 | class DefaultNamingStrategy implements NamingStrategyInterface |
9 | 9 | { |
10 | - private $beanPrefix = ''; |
|
11 | - private $beanSuffix = ''; |
|
12 | - private $baseBeanPrefix = 'Abstract'; |
|
13 | - private $baseBeanSuffix = ''; |
|
14 | - private $daoPrefix = ''; |
|
15 | - private $daoSuffix = 'Dao'; |
|
16 | - private $baseDaoPrefix = 'Abstract'; |
|
17 | - private $baseDaoSuffix = 'Dao'; |
|
18 | - private $exceptions = []; |
|
19 | - |
|
20 | - /** |
|
21 | - * Sets the string prefix to any bean class name. |
|
22 | - * |
|
23 | - * @param string $beanPrefix |
|
24 | - */ |
|
25 | - public function setBeanPrefix(string $beanPrefix) |
|
26 | - { |
|
27 | - $this->beanPrefix = $beanPrefix; |
|
28 | - } |
|
29 | - |
|
30 | - /** |
|
31 | - * Sets the string suffix to any bean class name. |
|
32 | - * |
|
33 | - * @param string $beanSuffix |
|
34 | - */ |
|
35 | - public function setBeanSuffix(string $beanSuffix) |
|
36 | - { |
|
37 | - $this->beanSuffix = $beanSuffix; |
|
38 | - } |
|
39 | - |
|
40 | - /** |
|
41 | - * Sets the string prefix to any base bean class name. |
|
42 | - * |
|
43 | - * @param string $baseBeanPrefix |
|
44 | - */ |
|
45 | - public function setBaseBeanPrefix(string $baseBeanPrefix) |
|
46 | - { |
|
47 | - $this->baseBeanPrefix = $baseBeanPrefix; |
|
48 | - } |
|
49 | - |
|
50 | - /** |
|
51 | - * Sets the string suffix to any base bean class name. |
|
52 | - * |
|
53 | - * @param string $baseBeanSuffix |
|
54 | - */ |
|
55 | - public function setBaseBeanSuffix(string $baseBeanSuffix) |
|
56 | - { |
|
57 | - $this->baseBeanSuffix = $baseBeanSuffix; |
|
58 | - } |
|
59 | - |
|
60 | - /** |
|
61 | - * Sets the string prefix to any DAO class name. |
|
62 | - * |
|
63 | - * @param string $daoPrefix |
|
64 | - */ |
|
65 | - public function setDaoPrefix(string $daoPrefix) |
|
66 | - { |
|
67 | - $this->daoPrefix = $daoPrefix; |
|
68 | - } |
|
69 | - |
|
70 | - /** |
|
71 | - * Sets the string suffix to any DAO class name. |
|
72 | - * |
|
73 | - * @param string $daoSuffix |
|
74 | - */ |
|
75 | - public function setDaoSuffix(string $daoSuffix) |
|
76 | - { |
|
77 | - $this->daoSuffix = $daoSuffix; |
|
78 | - } |
|
79 | - |
|
80 | - /** |
|
81 | - * Sets the string prefix to any base DAO class name. |
|
82 | - * |
|
83 | - * @param string $baseDaoPrefix |
|
84 | - */ |
|
85 | - public function setBaseDaoPrefix(string $baseDaoPrefix) |
|
86 | - { |
|
87 | - $this->baseDaoPrefix = $baseDaoPrefix; |
|
88 | - } |
|
89 | - |
|
90 | - /** |
|
91 | - * Sets the string suffix to any base DAO class name. |
|
92 | - * |
|
93 | - * @param string $baseDaoSuffix |
|
94 | - */ |
|
95 | - public function setBaseDaoSuffix(string $baseDaoSuffix) |
|
96 | - { |
|
97 | - $this->baseDaoSuffix = $baseDaoSuffix; |
|
98 | - } |
|
99 | - |
|
100 | - |
|
101 | - /** |
|
102 | - * Returns the bean class name from the table name (excluding the namespace). |
|
103 | - * |
|
104 | - * @param string $tableName |
|
105 | - * @return string |
|
106 | - */ |
|
107 | - public function getBeanClassName(string $tableName): string |
|
108 | - { |
|
109 | - return $this->beanPrefix.$this->toSingularCamelCase($tableName).$this->beanSuffix; |
|
110 | - } |
|
111 | - |
|
112 | - /** |
|
113 | - * Returns the base bean class name from the table name (excluding the namespace). |
|
114 | - * |
|
115 | - * @param string $tableName |
|
116 | - * @return string |
|
117 | - */ |
|
118 | - public function getBaseBeanClassName(string $tableName): string |
|
119 | - { |
|
120 | - return $this->baseBeanPrefix.$this->toSingularCamelCase($tableName).$this->baseBeanSuffix; |
|
121 | - } |
|
122 | - |
|
123 | - /** |
|
124 | - * Returns the name of the DAO class from the table name (excluding the namespace). |
|
125 | - * |
|
126 | - * @param string $tableName |
|
127 | - * @return string |
|
128 | - */ |
|
129 | - public function getDaoClassName(string $tableName): string |
|
130 | - { |
|
131 | - return $this->daoPrefix.$this->toSingularCamelCase($tableName).$this->daoSuffix; |
|
132 | - } |
|
133 | - |
|
134 | - /** |
|
135 | - * Returns the name of the base DAO class from the table name (excluding the namespace). |
|
136 | - * |
|
137 | - * @param string $tableName |
|
138 | - * @return string |
|
139 | - */ |
|
140 | - public function getBaseDaoClassName(string $tableName): string |
|
141 | - { |
|
142 | - return $this->baseDaoPrefix.$this->toSingularCamelCase($tableName).$this->baseDaoSuffix; |
|
143 | - } |
|
144 | - |
|
145 | - /** |
|
146 | - * Tries to put string to the singular form (if it is plural) and camel case form. |
|
147 | - * We assume the table names are in english. |
|
148 | - * |
|
149 | - * @param $str string |
|
150 | - * |
|
151 | - * @return string |
|
152 | - */ |
|
153 | - private function toSingularCamelCase(string $str): string |
|
154 | - { |
|
155 | - // Let's first check if this is not in the exceptions directory. |
|
156 | - if (isset($this->exceptions[$str])) { |
|
157 | - return $this->exceptions[$str]; |
|
158 | - } |
|
159 | - |
|
160 | - $tokens = preg_split("/[_ ]+/", $str); |
|
161 | - $tokens = array_map([Inflector::class, 'singularize'], $tokens); |
|
162 | - |
|
163 | - $str = ''; |
|
164 | - foreach ($tokens as $token) { |
|
165 | - $str .= ucfirst(Inflector::singularize($token)); |
|
166 | - } |
|
167 | - |
|
168 | - return $str; |
|
169 | - } |
|
170 | - |
|
171 | - /** |
|
172 | - * Returns the class name for the DAO factory. |
|
173 | - * |
|
174 | - * @return string |
|
175 | - */ |
|
176 | - public function getDaoFactoryClassName(): string |
|
177 | - { |
|
178 | - return 'DaoFactory'; |
|
179 | - } |
|
180 | - |
|
181 | - /** |
|
182 | - * Sets exceptions in the naming of classes. |
|
183 | - * The key is the name of the table, the value the "base" name of beans and DAOs. |
|
184 | - * |
|
185 | - * This is very useful for dealing with plural to singular translations in non english table names. |
|
186 | - * |
|
187 | - * For instance if you are dealing with a table containing horses in French ("chevaux" that has a singular "cheval"): |
|
188 | - * |
|
189 | - * [ |
|
190 | - * "chevaux" => "Cheval" |
|
191 | - * ] |
|
192 | - * |
|
193 | - * @param array<string,string> $exceptions |
|
194 | - */ |
|
195 | - public function setExceptions(array $exceptions) |
|
196 | - { |
|
197 | - $this->exceptions = $exceptions; |
|
198 | - } |
|
10 | + private $beanPrefix = ''; |
|
11 | + private $beanSuffix = ''; |
|
12 | + private $baseBeanPrefix = 'Abstract'; |
|
13 | + private $baseBeanSuffix = ''; |
|
14 | + private $daoPrefix = ''; |
|
15 | + private $daoSuffix = 'Dao'; |
|
16 | + private $baseDaoPrefix = 'Abstract'; |
|
17 | + private $baseDaoSuffix = 'Dao'; |
|
18 | + private $exceptions = []; |
|
19 | + |
|
20 | + /** |
|
21 | + * Sets the string prefix to any bean class name. |
|
22 | + * |
|
23 | + * @param string $beanPrefix |
|
24 | + */ |
|
25 | + public function setBeanPrefix(string $beanPrefix) |
|
26 | + { |
|
27 | + $this->beanPrefix = $beanPrefix; |
|
28 | + } |
|
29 | + |
|
30 | + /** |
|
31 | + * Sets the string suffix to any bean class name. |
|
32 | + * |
|
33 | + * @param string $beanSuffix |
|
34 | + */ |
|
35 | + public function setBeanSuffix(string $beanSuffix) |
|
36 | + { |
|
37 | + $this->beanSuffix = $beanSuffix; |
|
38 | + } |
|
39 | + |
|
40 | + /** |
|
41 | + * Sets the string prefix to any base bean class name. |
|
42 | + * |
|
43 | + * @param string $baseBeanPrefix |
|
44 | + */ |
|
45 | + public function setBaseBeanPrefix(string $baseBeanPrefix) |
|
46 | + { |
|
47 | + $this->baseBeanPrefix = $baseBeanPrefix; |
|
48 | + } |
|
49 | + |
|
50 | + /** |
|
51 | + * Sets the string suffix to any base bean class name. |
|
52 | + * |
|
53 | + * @param string $baseBeanSuffix |
|
54 | + */ |
|
55 | + public function setBaseBeanSuffix(string $baseBeanSuffix) |
|
56 | + { |
|
57 | + $this->baseBeanSuffix = $baseBeanSuffix; |
|
58 | + } |
|
59 | + |
|
60 | + /** |
|
61 | + * Sets the string prefix to any DAO class name. |
|
62 | + * |
|
63 | + * @param string $daoPrefix |
|
64 | + */ |
|
65 | + public function setDaoPrefix(string $daoPrefix) |
|
66 | + { |
|
67 | + $this->daoPrefix = $daoPrefix; |
|
68 | + } |
|
69 | + |
|
70 | + /** |
|
71 | + * Sets the string suffix to any DAO class name. |
|
72 | + * |
|
73 | + * @param string $daoSuffix |
|
74 | + */ |
|
75 | + public function setDaoSuffix(string $daoSuffix) |
|
76 | + { |
|
77 | + $this->daoSuffix = $daoSuffix; |
|
78 | + } |
|
79 | + |
|
80 | + /** |
|
81 | + * Sets the string prefix to any base DAO class name. |
|
82 | + * |
|
83 | + * @param string $baseDaoPrefix |
|
84 | + */ |
|
85 | + public function setBaseDaoPrefix(string $baseDaoPrefix) |
|
86 | + { |
|
87 | + $this->baseDaoPrefix = $baseDaoPrefix; |
|
88 | + } |
|
89 | + |
|
90 | + /** |
|
91 | + * Sets the string suffix to any base DAO class name. |
|
92 | + * |
|
93 | + * @param string $baseDaoSuffix |
|
94 | + */ |
|
95 | + public function setBaseDaoSuffix(string $baseDaoSuffix) |
|
96 | + { |
|
97 | + $this->baseDaoSuffix = $baseDaoSuffix; |
|
98 | + } |
|
99 | + |
|
100 | + |
|
101 | + /** |
|
102 | + * Returns the bean class name from the table name (excluding the namespace). |
|
103 | + * |
|
104 | + * @param string $tableName |
|
105 | + * @return string |
|
106 | + */ |
|
107 | + public function getBeanClassName(string $tableName): string |
|
108 | + { |
|
109 | + return $this->beanPrefix.$this->toSingularCamelCase($tableName).$this->beanSuffix; |
|
110 | + } |
|
111 | + |
|
112 | + /** |
|
113 | + * Returns the base bean class name from the table name (excluding the namespace). |
|
114 | + * |
|
115 | + * @param string $tableName |
|
116 | + * @return string |
|
117 | + */ |
|
118 | + public function getBaseBeanClassName(string $tableName): string |
|
119 | + { |
|
120 | + return $this->baseBeanPrefix.$this->toSingularCamelCase($tableName).$this->baseBeanSuffix; |
|
121 | + } |
|
122 | + |
|
123 | + /** |
|
124 | + * Returns the name of the DAO class from the table name (excluding the namespace). |
|
125 | + * |
|
126 | + * @param string $tableName |
|
127 | + * @return string |
|
128 | + */ |
|
129 | + public function getDaoClassName(string $tableName): string |
|
130 | + { |
|
131 | + return $this->daoPrefix.$this->toSingularCamelCase($tableName).$this->daoSuffix; |
|
132 | + } |
|
133 | + |
|
134 | + /** |
|
135 | + * Returns the name of the base DAO class from the table name (excluding the namespace). |
|
136 | + * |
|
137 | + * @param string $tableName |
|
138 | + * @return string |
|
139 | + */ |
|
140 | + public function getBaseDaoClassName(string $tableName): string |
|
141 | + { |
|
142 | + return $this->baseDaoPrefix.$this->toSingularCamelCase($tableName).$this->baseDaoSuffix; |
|
143 | + } |
|
144 | + |
|
145 | + /** |
|
146 | + * Tries to put string to the singular form (if it is plural) and camel case form. |
|
147 | + * We assume the table names are in english. |
|
148 | + * |
|
149 | + * @param $str string |
|
150 | + * |
|
151 | + * @return string |
|
152 | + */ |
|
153 | + private function toSingularCamelCase(string $str): string |
|
154 | + { |
|
155 | + // Let's first check if this is not in the exceptions directory. |
|
156 | + if (isset($this->exceptions[$str])) { |
|
157 | + return $this->exceptions[$str]; |
|
158 | + } |
|
159 | + |
|
160 | + $tokens = preg_split("/[_ ]+/", $str); |
|
161 | + $tokens = array_map([Inflector::class, 'singularize'], $tokens); |
|
162 | + |
|
163 | + $str = ''; |
|
164 | + foreach ($tokens as $token) { |
|
165 | + $str .= ucfirst(Inflector::singularize($token)); |
|
166 | + } |
|
167 | + |
|
168 | + return $str; |
|
169 | + } |
|
170 | + |
|
171 | + /** |
|
172 | + * Returns the class name for the DAO factory. |
|
173 | + * |
|
174 | + * @return string |
|
175 | + */ |
|
176 | + public function getDaoFactoryClassName(): string |
|
177 | + { |
|
178 | + return 'DaoFactory'; |
|
179 | + } |
|
180 | + |
|
181 | + /** |
|
182 | + * Sets exceptions in the naming of classes. |
|
183 | + * The key is the name of the table, the value the "base" name of beans and DAOs. |
|
184 | + * |
|
185 | + * This is very useful for dealing with plural to singular translations in non english table names. |
|
186 | + * |
|
187 | + * For instance if you are dealing with a table containing horses in French ("chevaux" that has a singular "cheval"): |
|
188 | + * |
|
189 | + * [ |
|
190 | + * "chevaux" => "Cheval" |
|
191 | + * ] |
|
192 | + * |
|
193 | + * @param array<string,string> $exceptions |
|
194 | + */ |
|
195 | + public function setExceptions(array $exceptions) |
|
196 | + { |
|
197 | + $this->exceptions = $exceptions; |
|
198 | + } |
|
199 | 199 | } |
@@ -15,168 +15,168 @@ |
||
15 | 15 | */ |
16 | 16 | class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory |
17 | 17 | { |
18 | - private $mainTable; |
|
19 | - private $from; |
|
20 | - private $filterString; |
|
21 | - private $cache; |
|
22 | - private $cachePrefix; |
|
23 | - |
|
24 | - public function __construct(string $mainTable, string $from, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, SchemaAnalyzer $schemaAnalyzer, Cache $cache, string $cachePrefix) |
|
25 | - { |
|
26 | - parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy); |
|
27 | - $this->mainTable = $mainTable; |
|
28 | - $this->from = $from; |
|
29 | - $this->filterString = $filterString; |
|
30 | - $this->schemaAnalyzer = $schemaAnalyzer; |
|
31 | - $this->cache = $cache; |
|
32 | - $this->cachePrefix = $cachePrefix; |
|
33 | - } |
|
34 | - |
|
35 | - protected function compute() |
|
36 | - { |
|
37 | - $connection = $this->tdbmService->getConnection(); |
|
38 | - |
|
39 | - $columnsList = null; |
|
40 | - |
|
41 | - $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($this->mainTable); |
|
42 | - |
|
43 | - list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy); |
|
44 | - |
|
45 | - $sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM '.$this->from; |
|
46 | - |
|
47 | - // Let's compute the COUNT. |
|
48 | - $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns(); |
|
49 | - $pkColumnNames = array_map(function ($pkColumn) { |
|
50 | - return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn); |
|
51 | - }, $pkColumnNames); |
|
52 | - |
|
53 | - $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM '.$this->from; |
|
54 | - |
|
55 | - // Add joins on inherited tables if necessary |
|
56 | - if (count($allFetchedTables) > 1) { |
|
57 | - $joinSql = ''; |
|
58 | - $parentFks = $this->getParentRelationshipForeignKeys($this->mainTable); |
|
59 | - foreach ($parentFks as $fk) { |
|
60 | - $joinSql .= sprintf(' JOIN %s ON (%s.%s = %s.%s)', |
|
61 | - $connection->quoteIdentifier($fk->getForeignTableName()), |
|
62 | - $connection->quoteIdentifier($fk->getLocalTableName()), |
|
63 | - $connection->quoteIdentifier($fk->getLocalColumns()[0]), |
|
64 | - $connection->quoteIdentifier($fk->getForeignTableName()), |
|
65 | - $connection->quoteIdentifier($fk->getForeignColumns()[0]) |
|
66 | - ); |
|
67 | - } |
|
68 | - |
|
69 | - $childrenFks = $this->getChildrenRelationshipForeignKeys($this->mainTable); |
|
70 | - foreach ($childrenFks as $fk) { |
|
71 | - $joinSql .= sprintf(' LEFT JOIN %s ON (%s.%s = %s.%s)', |
|
72 | - $connection->quoteIdentifier($fk->getLocalTableName()), |
|
73 | - $connection->quoteIdentifier($fk->getForeignTableName()), |
|
74 | - $connection->quoteIdentifier($fk->getForeignColumns()[0]), |
|
75 | - $connection->quoteIdentifier($fk->getLocalTableName()), |
|
76 | - $connection->quoteIdentifier($fk->getLocalColumns()[0]) |
|
77 | - ); |
|
78 | - } |
|
79 | - |
|
80 | - $sql .= $joinSql; |
|
81 | - } |
|
82 | - |
|
83 | - if (!empty($this->filterString)) { |
|
84 | - $sql .= ' WHERE '.$this->filterString; |
|
85 | - $countSql .= ' WHERE '.$this->filterString; |
|
86 | - } |
|
87 | - |
|
88 | - if (!empty($orderString)) { |
|
89 | - $sql .= ' ORDER BY '.$orderString; |
|
90 | - } |
|
91 | - |
|
92 | - if (stripos($countSql, 'GROUP BY') !== false) { |
|
93 | - throw new TDBMException('Unsupported use of GROUP BY in SQL request.'); |
|
94 | - } |
|
95 | - |
|
96 | - $this->magicSql = $sql; |
|
97 | - $this->magicSqlCount = $countSql; |
|
98 | - $this->columnDescList = $columnDescList; |
|
99 | - } |
|
100 | - |
|
101 | - /** |
|
102 | - * @param string $tableName |
|
103 | - * |
|
104 | - * @return ForeignKeyConstraint[] |
|
105 | - */ |
|
106 | - private function getParentRelationshipForeignKeys($tableName) |
|
107 | - { |
|
108 | - return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) { |
|
109 | - return $this->getParentRelationshipForeignKeysWithoutCache($tableName); |
|
110 | - }); |
|
111 | - } |
|
112 | - |
|
113 | - /** |
|
114 | - * @param string $tableName |
|
115 | - * |
|
116 | - * @return ForeignKeyConstraint[] |
|
117 | - */ |
|
118 | - private function getParentRelationshipForeignKeysWithoutCache($tableName) |
|
119 | - { |
|
120 | - $parentFks = []; |
|
121 | - $currentTable = $tableName; |
|
122 | - while ($currentFk = $this->schemaAnalyzer->getParentRelationship($currentTable)) { |
|
123 | - $currentTable = $currentFk->getForeignTableName(); |
|
124 | - $parentFks[] = $currentFk; |
|
125 | - } |
|
126 | - |
|
127 | - return $parentFks; |
|
128 | - } |
|
129 | - |
|
130 | - /** |
|
131 | - * @param string $tableName |
|
132 | - * |
|
133 | - * @return ForeignKeyConstraint[] |
|
134 | - */ |
|
135 | - private function getChildrenRelationshipForeignKeys(string $tableName) : array |
|
136 | - { |
|
137 | - return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) { |
|
138 | - return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName); |
|
139 | - }); |
|
140 | - } |
|
141 | - |
|
142 | - /** |
|
143 | - * @param string $tableName |
|
144 | - * |
|
145 | - * @return ForeignKeyConstraint[] |
|
146 | - */ |
|
147 | - private function getChildrenRelationshipForeignKeysWithoutCache(string $tableName) : array |
|
148 | - { |
|
149 | - $children = $this->schemaAnalyzer->getChildrenRelationships($tableName); |
|
150 | - |
|
151 | - if (!empty($children)) { |
|
152 | - $fksTables = array_map(function (ForeignKeyConstraint $fk) { |
|
153 | - return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName()); |
|
154 | - }, $children); |
|
155 | - |
|
156 | - $fks = array_merge($children, call_user_func_array('array_merge', $fksTables)); |
|
157 | - |
|
158 | - return $fks; |
|
159 | - } else { |
|
160 | - return []; |
|
161 | - } |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * Returns an item from cache or computes it using $closure and puts it in cache. |
|
166 | - * |
|
167 | - * @param string $key |
|
168 | - * @param callable $closure |
|
169 | - * |
|
170 | - * @return mixed |
|
171 | - */ |
|
172 | - protected function fromCache(string $key, callable $closure) |
|
173 | - { |
|
174 | - $item = $this->cache->fetch($key); |
|
175 | - if ($item === false) { |
|
176 | - $item = $closure(); |
|
177 | - $this->cache->save($key, $item); |
|
178 | - } |
|
179 | - |
|
180 | - return $item; |
|
181 | - } |
|
18 | + private $mainTable; |
|
19 | + private $from; |
|
20 | + private $filterString; |
|
21 | + private $cache; |
|
22 | + private $cachePrefix; |
|
23 | + |
|
24 | + public function __construct(string $mainTable, string $from, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, SchemaAnalyzer $schemaAnalyzer, Cache $cache, string $cachePrefix) |
|
25 | + { |
|
26 | + parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy); |
|
27 | + $this->mainTable = $mainTable; |
|
28 | + $this->from = $from; |
|
29 | + $this->filterString = $filterString; |
|
30 | + $this->schemaAnalyzer = $schemaAnalyzer; |
|
31 | + $this->cache = $cache; |
|
32 | + $this->cachePrefix = $cachePrefix; |
|
33 | + } |
|
34 | + |
|
35 | + protected function compute() |
|
36 | + { |
|
37 | + $connection = $this->tdbmService->getConnection(); |
|
38 | + |
|
39 | + $columnsList = null; |
|
40 | + |
|
41 | + $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($this->mainTable); |
|
42 | + |
|
43 | + list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy); |
|
44 | + |
|
45 | + $sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM '.$this->from; |
|
46 | + |
|
47 | + // Let's compute the COUNT. |
|
48 | + $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns(); |
|
49 | + $pkColumnNames = array_map(function ($pkColumn) { |
|
50 | + return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn); |
|
51 | + }, $pkColumnNames); |
|
52 | + |
|
53 | + $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM '.$this->from; |
|
54 | + |
|
55 | + // Add joins on inherited tables if necessary |
|
56 | + if (count($allFetchedTables) > 1) { |
|
57 | + $joinSql = ''; |
|
58 | + $parentFks = $this->getParentRelationshipForeignKeys($this->mainTable); |
|
59 | + foreach ($parentFks as $fk) { |
|
60 | + $joinSql .= sprintf(' JOIN %s ON (%s.%s = %s.%s)', |
|
61 | + $connection->quoteIdentifier($fk->getForeignTableName()), |
|
62 | + $connection->quoteIdentifier($fk->getLocalTableName()), |
|
63 | + $connection->quoteIdentifier($fk->getLocalColumns()[0]), |
|
64 | + $connection->quoteIdentifier($fk->getForeignTableName()), |
|
65 | + $connection->quoteIdentifier($fk->getForeignColumns()[0]) |
|
66 | + ); |
|
67 | + } |
|
68 | + |
|
69 | + $childrenFks = $this->getChildrenRelationshipForeignKeys($this->mainTable); |
|
70 | + foreach ($childrenFks as $fk) { |
|
71 | + $joinSql .= sprintf(' LEFT JOIN %s ON (%s.%s = %s.%s)', |
|
72 | + $connection->quoteIdentifier($fk->getLocalTableName()), |
|
73 | + $connection->quoteIdentifier($fk->getForeignTableName()), |
|
74 | + $connection->quoteIdentifier($fk->getForeignColumns()[0]), |
|
75 | + $connection->quoteIdentifier($fk->getLocalTableName()), |
|
76 | + $connection->quoteIdentifier($fk->getLocalColumns()[0]) |
|
77 | + ); |
|
78 | + } |
|
79 | + |
|
80 | + $sql .= $joinSql; |
|
81 | + } |
|
82 | + |
|
83 | + if (!empty($this->filterString)) { |
|
84 | + $sql .= ' WHERE '.$this->filterString; |
|
85 | + $countSql .= ' WHERE '.$this->filterString; |
|
86 | + } |
|
87 | + |
|
88 | + if (!empty($orderString)) { |
|
89 | + $sql .= ' ORDER BY '.$orderString; |
|
90 | + } |
|
91 | + |
|
92 | + if (stripos($countSql, 'GROUP BY') !== false) { |
|
93 | + throw new TDBMException('Unsupported use of GROUP BY in SQL request.'); |
|
94 | + } |
|
95 | + |
|
96 | + $this->magicSql = $sql; |
|
97 | + $this->magicSqlCount = $countSql; |
|
98 | + $this->columnDescList = $columnDescList; |
|
99 | + } |
|
100 | + |
|
101 | + /** |
|
102 | + * @param string $tableName |
|
103 | + * |
|
104 | + * @return ForeignKeyConstraint[] |
|
105 | + */ |
|
106 | + private function getParentRelationshipForeignKeys($tableName) |
|
107 | + { |
|
108 | + return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) { |
|
109 | + return $this->getParentRelationshipForeignKeysWithoutCache($tableName); |
|
110 | + }); |
|
111 | + } |
|
112 | + |
|
113 | + /** |
|
114 | + * @param string $tableName |
|
115 | + * |
|
116 | + * @return ForeignKeyConstraint[] |
|
117 | + */ |
|
118 | + private function getParentRelationshipForeignKeysWithoutCache($tableName) |
|
119 | + { |
|
120 | + $parentFks = []; |
|
121 | + $currentTable = $tableName; |
|
122 | + while ($currentFk = $this->schemaAnalyzer->getParentRelationship($currentTable)) { |
|
123 | + $currentTable = $currentFk->getForeignTableName(); |
|
124 | + $parentFks[] = $currentFk; |
|
125 | + } |
|
126 | + |
|
127 | + return $parentFks; |
|
128 | + } |
|
129 | + |
|
130 | + /** |
|
131 | + * @param string $tableName |
|
132 | + * |
|
133 | + * @return ForeignKeyConstraint[] |
|
134 | + */ |
|
135 | + private function getChildrenRelationshipForeignKeys(string $tableName) : array |
|
136 | + { |
|
137 | + return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) { |
|
138 | + return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName); |
|
139 | + }); |
|
140 | + } |
|
141 | + |
|
142 | + /** |
|
143 | + * @param string $tableName |
|
144 | + * |
|
145 | + * @return ForeignKeyConstraint[] |
|
146 | + */ |
|
147 | + private function getChildrenRelationshipForeignKeysWithoutCache(string $tableName) : array |
|
148 | + { |
|
149 | + $children = $this->schemaAnalyzer->getChildrenRelationships($tableName); |
|
150 | + |
|
151 | + if (!empty($children)) { |
|
152 | + $fksTables = array_map(function (ForeignKeyConstraint $fk) { |
|
153 | + return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName()); |
|
154 | + }, $children); |
|
155 | + |
|
156 | + $fks = array_merge($children, call_user_func_array('array_merge', $fksTables)); |
|
157 | + |
|
158 | + return $fks; |
|
159 | + } else { |
|
160 | + return []; |
|
161 | + } |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * Returns an item from cache or computes it using $closure and puts it in cache. |
|
166 | + * |
|
167 | + * @param string $key |
|
168 | + * @param callable $closure |
|
169 | + * |
|
170 | + * @return mixed |
|
171 | + */ |
|
172 | + protected function fromCache(string $key, callable $closure) |
|
173 | + { |
|
174 | + $item = $this->cache->fetch($key); |
|
175 | + if ($item === false) { |
|
176 | + $item = $closure(); |
|
177 | + $this->cache->save($key, $item); |
|
178 | + } |
|
179 | + |
|
180 | + return $item; |
|
181 | + } |
|
182 | 182 | } |
@@ -29,271 +29,271 @@ |
||
29 | 29 | */ |
30 | 30 | class InnerResultIterator implements \Iterator, \Countable, \ArrayAccess |
31 | 31 | { |
32 | - /** |
|
33 | - * @var Statement |
|
34 | - */ |
|
35 | - protected $statement; |
|
36 | - |
|
37 | - protected $fetchStarted = false; |
|
38 | - private $objectStorage; |
|
39 | - private $className; |
|
40 | - |
|
41 | - private $tdbmService; |
|
42 | - private $magicSql; |
|
43 | - private $parameters; |
|
44 | - private $limit; |
|
45 | - private $offset; |
|
46 | - private $columnDescriptors; |
|
47 | - private $magicQuery; |
|
48 | - |
|
49 | - /** |
|
50 | - * The key of the current retrieved object. |
|
51 | - * |
|
52 | - * @var int |
|
53 | - */ |
|
54 | - protected $key = -1; |
|
55 | - |
|
56 | - protected $current = null; |
|
57 | - |
|
58 | - private $databasePlatform; |
|
59 | - |
|
60 | - /** |
|
61 | - * @var LoggerInterface |
|
62 | - */ |
|
63 | - private $logger; |
|
64 | - |
|
65 | - public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger) |
|
66 | - { |
|
67 | - $this->magicSql = $magicSql; |
|
68 | - $this->objectStorage = $objectStorage; |
|
69 | - $this->className = $className; |
|
70 | - $this->tdbmService = $tdbmService; |
|
71 | - $this->parameters = $parameters; |
|
72 | - $this->limit = $limit; |
|
73 | - $this->offset = $offset; |
|
74 | - $this->columnDescriptors = $columnDescriptors; |
|
75 | - $this->magicQuery = $magicQuery; |
|
76 | - $this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform(); |
|
77 | - $this->logger = $logger; |
|
78 | - } |
|
79 | - |
|
80 | - protected function executeQuery() |
|
81 | - { |
|
82 | - $sql = $this->magicQuery->build($this->magicSql, $this->parameters); |
|
83 | - $sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset); |
|
84 | - |
|
85 | - $this->logger->debug('Running SQL request: '.$sql); |
|
86 | - |
|
87 | - $this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters); |
|
88 | - |
|
89 | - $this->fetchStarted = true; |
|
90 | - } |
|
91 | - |
|
92 | - /** |
|
93 | - * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings). |
|
94 | - * |
|
95 | - * @return int |
|
96 | - */ |
|
97 | - public function count() |
|
98 | - { |
|
99 | - if (!$this->fetchStarted) { |
|
100 | - $this->executeQuery(); |
|
101 | - } |
|
102 | - |
|
103 | - return $this->statement->rowCount(); |
|
104 | - } |
|
105 | - |
|
106 | - /** |
|
107 | - * Fetches record at current cursor. |
|
108 | - * |
|
109 | - * @return AbstractTDBMObject|null |
|
110 | - */ |
|
111 | - public function current() |
|
112 | - { |
|
113 | - return $this->current; |
|
114 | - } |
|
115 | - |
|
116 | - /** |
|
117 | - * Returns the current result's key. |
|
118 | - * |
|
119 | - * @return int |
|
120 | - */ |
|
121 | - public function key() |
|
122 | - { |
|
123 | - return $this->key; |
|
124 | - } |
|
125 | - |
|
126 | - /** |
|
127 | - * Advances the cursor to the next result. |
|
128 | - * Casts the database result into one (or several) beans. |
|
129 | - */ |
|
130 | - public function next() |
|
131 | - { |
|
132 | - $row = $this->statement->fetch(\PDO::FETCH_LAZY); |
|
133 | - if ($row) { |
|
134 | - |
|
135 | - // array<tablegroup, array<table, array<column, value>>> |
|
136 | - $beansData = []; |
|
137 | - foreach ($row as $key => $value) { |
|
138 | - if (!isset($this->columnDescriptors[$key])) { |
|
139 | - continue; |
|
140 | - } |
|
141 | - |
|
142 | - $columnDescriptor = $this->columnDescriptors[$key]; |
|
143 | - |
|
144 | - if ($columnDescriptor['tableGroup'] === null) { |
|
145 | - // A column can have no tableGroup (if it comes from an ORDER BY expression) |
|
146 | - continue; |
|
147 | - } |
|
148 | - |
|
149 | - // Let's cast the value according to its type |
|
150 | - $value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform); |
|
151 | - |
|
152 | - $beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value; |
|
153 | - } |
|
154 | - |
|
155 | - $reflectionClassCache = []; |
|
156 | - $firstBean = true; |
|
157 | - foreach ($beansData as $beanData) { |
|
158 | - |
|
159 | - // Let's find the bean class name associated to the bean. |
|
160 | - |
|
161 | - list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData); |
|
162 | - |
|
163 | - if ($this->className !== null) { |
|
164 | - $actualClassName = $this->className; |
|
165 | - } |
|
166 | - |
|
167 | - // Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched: |
|
168 | - foreach ($beanData as $tableName => $descriptors) { |
|
169 | - if (!in_array($tableName, $tablesUsed)) { |
|
170 | - unset($beanData[$tableName]); |
|
171 | - } |
|
172 | - } |
|
173 | - |
|
174 | - // Must we create the bean? Let's see in the cache if we have a mapping DbRow? |
|
175 | - // Let's get the first object mapping a row: |
|
176 | - // We do this loop only for the first table |
|
177 | - |
|
178 | - $primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]); |
|
179 | - $hash = $this->tdbmService->getObjectHash($primaryKeys); |
|
180 | - |
|
181 | - if ($this->objectStorage->has($mainBeanTableName, $hash)) { |
|
182 | - $dbRow = $this->objectStorage->get($mainBeanTableName, $hash); |
|
183 | - $bean = $dbRow->getTDBMObject(); |
|
184 | - } else { |
|
185 | - // Let's construct the bean |
|
186 | - if (!isset($reflectionClassCache[$actualClassName])) { |
|
187 | - $reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName); |
|
188 | - } |
|
189 | - // Let's bypass the constructor when creating the bean! |
|
190 | - $bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor(); |
|
191 | - $bean->_constructFromData($beanData, $this->tdbmService); |
|
192 | - } |
|
193 | - |
|
194 | - // The first bean is the one containing the main table. |
|
195 | - if ($firstBean) { |
|
196 | - $firstBean = false; |
|
197 | - $this->current = $bean; |
|
198 | - } |
|
199 | - } |
|
200 | - |
|
201 | - ++$this->key; |
|
202 | - } else { |
|
203 | - $this->current = null; |
|
204 | - } |
|
205 | - } |
|
206 | - |
|
207 | - /** |
|
208 | - * Moves the cursor to the beginning of the result set. |
|
209 | - */ |
|
210 | - public function rewind() |
|
211 | - { |
|
212 | - $this->executeQuery(); |
|
213 | - $this->key = -1; |
|
214 | - $this->next(); |
|
215 | - } |
|
216 | - /** |
|
217 | - * Checks if the cursor is reading a valid result. |
|
218 | - * |
|
219 | - * @return bool |
|
220 | - */ |
|
221 | - public function valid() |
|
222 | - { |
|
223 | - return $this->current !== null; |
|
224 | - } |
|
225 | - |
|
226 | - /** |
|
227 | - * Whether a offset exists. |
|
228 | - * |
|
229 | - * @link http://php.net/manual/en/arrayaccess.offsetexists.php |
|
230 | - * |
|
231 | - * @param mixed $offset <p> |
|
232 | - * An offset to check for. |
|
233 | - * </p> |
|
234 | - * |
|
235 | - * @return bool true on success or false on failure. |
|
236 | - * </p> |
|
237 | - * <p> |
|
238 | - * The return value will be casted to boolean if non-boolean was returned |
|
239 | - * |
|
240 | - * @since 5.0.0 |
|
241 | - */ |
|
242 | - public function offsetExists($offset) |
|
243 | - { |
|
244 | - throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.'); |
|
245 | - } |
|
246 | - |
|
247 | - /** |
|
248 | - * Offset to retrieve. |
|
249 | - * |
|
250 | - * @link http://php.net/manual/en/arrayaccess.offsetget.php |
|
251 | - * |
|
252 | - * @param mixed $offset <p> |
|
253 | - * The offset to retrieve. |
|
254 | - * </p> |
|
255 | - * |
|
256 | - * @return mixed Can return all value types |
|
257 | - * |
|
258 | - * @since 5.0.0 |
|
259 | - */ |
|
260 | - public function offsetGet($offset) |
|
261 | - { |
|
262 | - throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.'); |
|
263 | - } |
|
264 | - |
|
265 | - /** |
|
266 | - * Offset to set. |
|
267 | - * |
|
268 | - * @link http://php.net/manual/en/arrayaccess.offsetset.php |
|
269 | - * |
|
270 | - * @param mixed $offset <p> |
|
271 | - * The offset to assign the value to. |
|
272 | - * </p> |
|
273 | - * @param mixed $value <p> |
|
274 | - * The value to set. |
|
275 | - * </p> |
|
276 | - * |
|
277 | - * @since 5.0.0 |
|
278 | - */ |
|
279 | - public function offsetSet($offset, $value) |
|
280 | - { |
|
281 | - throw new TDBMInvalidOperationException('You can set values in a TDBM result set.'); |
|
282 | - } |
|
283 | - |
|
284 | - /** |
|
285 | - * Offset to unset. |
|
286 | - * |
|
287 | - * @link http://php.net/manual/en/arrayaccess.offsetunset.php |
|
288 | - * |
|
289 | - * @param mixed $offset <p> |
|
290 | - * The offset to unset. |
|
291 | - * </p> |
|
292 | - * |
|
293 | - * @since 5.0.0 |
|
294 | - */ |
|
295 | - public function offsetUnset($offset) |
|
296 | - { |
|
297 | - throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.'); |
|
298 | - } |
|
32 | + /** |
|
33 | + * @var Statement |
|
34 | + */ |
|
35 | + protected $statement; |
|
36 | + |
|
37 | + protected $fetchStarted = false; |
|
38 | + private $objectStorage; |
|
39 | + private $className; |
|
40 | + |
|
41 | + private $tdbmService; |
|
42 | + private $magicSql; |
|
43 | + private $parameters; |
|
44 | + private $limit; |
|
45 | + private $offset; |
|
46 | + private $columnDescriptors; |
|
47 | + private $magicQuery; |
|
48 | + |
|
49 | + /** |
|
50 | + * The key of the current retrieved object. |
|
51 | + * |
|
52 | + * @var int |
|
53 | + */ |
|
54 | + protected $key = -1; |
|
55 | + |
|
56 | + protected $current = null; |
|
57 | + |
|
58 | + private $databasePlatform; |
|
59 | + |
|
60 | + /** |
|
61 | + * @var LoggerInterface |
|
62 | + */ |
|
63 | + private $logger; |
|
64 | + |
|
65 | + public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger) |
|
66 | + { |
|
67 | + $this->magicSql = $magicSql; |
|
68 | + $this->objectStorage = $objectStorage; |
|
69 | + $this->className = $className; |
|
70 | + $this->tdbmService = $tdbmService; |
|
71 | + $this->parameters = $parameters; |
|
72 | + $this->limit = $limit; |
|
73 | + $this->offset = $offset; |
|
74 | + $this->columnDescriptors = $columnDescriptors; |
|
75 | + $this->magicQuery = $magicQuery; |
|
76 | + $this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform(); |
|
77 | + $this->logger = $logger; |
|
78 | + } |
|
79 | + |
|
80 | + protected function executeQuery() |
|
81 | + { |
|
82 | + $sql = $this->magicQuery->build($this->magicSql, $this->parameters); |
|
83 | + $sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset); |
|
84 | + |
|
85 | + $this->logger->debug('Running SQL request: '.$sql); |
|
86 | + |
|
87 | + $this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters); |
|
88 | + |
|
89 | + $this->fetchStarted = true; |
|
90 | + } |
|
91 | + |
|
92 | + /** |
|
93 | + * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings). |
|
94 | + * |
|
95 | + * @return int |
|
96 | + */ |
|
97 | + public function count() |
|
98 | + { |
|
99 | + if (!$this->fetchStarted) { |
|
100 | + $this->executeQuery(); |
|
101 | + } |
|
102 | + |
|
103 | + return $this->statement->rowCount(); |
|
104 | + } |
|
105 | + |
|
106 | + /** |
|
107 | + * Fetches record at current cursor. |
|
108 | + * |
|
109 | + * @return AbstractTDBMObject|null |
|
110 | + */ |
|
111 | + public function current() |
|
112 | + { |
|
113 | + return $this->current; |
|
114 | + } |
|
115 | + |
|
116 | + /** |
|
117 | + * Returns the current result's key. |
|
118 | + * |
|
119 | + * @return int |
|
120 | + */ |
|
121 | + public function key() |
|
122 | + { |
|
123 | + return $this->key; |
|
124 | + } |
|
125 | + |
|
126 | + /** |
|
127 | + * Advances the cursor to the next result. |
|
128 | + * Casts the database result into one (or several) beans. |
|
129 | + */ |
|
130 | + public function next() |
|
131 | + { |
|
132 | + $row = $this->statement->fetch(\PDO::FETCH_LAZY); |
|
133 | + if ($row) { |
|
134 | + |
|
135 | + // array<tablegroup, array<table, array<column, value>>> |
|
136 | + $beansData = []; |
|
137 | + foreach ($row as $key => $value) { |
|
138 | + if (!isset($this->columnDescriptors[$key])) { |
|
139 | + continue; |
|
140 | + } |
|
141 | + |
|
142 | + $columnDescriptor = $this->columnDescriptors[$key]; |
|
143 | + |
|
144 | + if ($columnDescriptor['tableGroup'] === null) { |
|
145 | + // A column can have no tableGroup (if it comes from an ORDER BY expression) |
|
146 | + continue; |
|
147 | + } |
|
148 | + |
|
149 | + // Let's cast the value according to its type |
|
150 | + $value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform); |
|
151 | + |
|
152 | + $beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value; |
|
153 | + } |
|
154 | + |
|
155 | + $reflectionClassCache = []; |
|
156 | + $firstBean = true; |
|
157 | + foreach ($beansData as $beanData) { |
|
158 | + |
|
159 | + // Let's find the bean class name associated to the bean. |
|
160 | + |
|
161 | + list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData); |
|
162 | + |
|
163 | + if ($this->className !== null) { |
|
164 | + $actualClassName = $this->className; |
|
165 | + } |
|
166 | + |
|
167 | + // Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched: |
|
168 | + foreach ($beanData as $tableName => $descriptors) { |
|
169 | + if (!in_array($tableName, $tablesUsed)) { |
|
170 | + unset($beanData[$tableName]); |
|
171 | + } |
|
172 | + } |
|
173 | + |
|
174 | + // Must we create the bean? Let's see in the cache if we have a mapping DbRow? |
|
175 | + // Let's get the first object mapping a row: |
|
176 | + // We do this loop only for the first table |
|
177 | + |
|
178 | + $primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]); |
|
179 | + $hash = $this->tdbmService->getObjectHash($primaryKeys); |
|
180 | + |
|
181 | + if ($this->objectStorage->has($mainBeanTableName, $hash)) { |
|
182 | + $dbRow = $this->objectStorage->get($mainBeanTableName, $hash); |
|
183 | + $bean = $dbRow->getTDBMObject(); |
|
184 | + } else { |
|
185 | + // Let's construct the bean |
|
186 | + if (!isset($reflectionClassCache[$actualClassName])) { |
|
187 | + $reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName); |
|
188 | + } |
|
189 | + // Let's bypass the constructor when creating the bean! |
|
190 | + $bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor(); |
|
191 | + $bean->_constructFromData($beanData, $this->tdbmService); |
|
192 | + } |
|
193 | + |
|
194 | + // The first bean is the one containing the main table. |
|
195 | + if ($firstBean) { |
|
196 | + $firstBean = false; |
|
197 | + $this->current = $bean; |
|
198 | + } |
|
199 | + } |
|
200 | + |
|
201 | + ++$this->key; |
|
202 | + } else { |
|
203 | + $this->current = null; |
|
204 | + } |
|
205 | + } |
|
206 | + |
|
207 | + /** |
|
208 | + * Moves the cursor to the beginning of the result set. |
|
209 | + */ |
|
210 | + public function rewind() |
|
211 | + { |
|
212 | + $this->executeQuery(); |
|
213 | + $this->key = -1; |
|
214 | + $this->next(); |
|
215 | + } |
|
216 | + /** |
|
217 | + * Checks if the cursor is reading a valid result. |
|
218 | + * |
|
219 | + * @return bool |
|
220 | + */ |
|
221 | + public function valid() |
|
222 | + { |
|
223 | + return $this->current !== null; |
|
224 | + } |
|
225 | + |
|
226 | + /** |
|
227 | + * Whether a offset exists. |
|
228 | + * |
|
229 | + * @link http://php.net/manual/en/arrayaccess.offsetexists.php |
|
230 | + * |
|
231 | + * @param mixed $offset <p> |
|
232 | + * An offset to check for. |
|
233 | + * </p> |
|
234 | + * |
|
235 | + * @return bool true on success or false on failure. |
|
236 | + * </p> |
|
237 | + * <p> |
|
238 | + * The return value will be casted to boolean if non-boolean was returned |
|
239 | + * |
|
240 | + * @since 5.0.0 |
|
241 | + */ |
|
242 | + public function offsetExists($offset) |
|
243 | + { |
|
244 | + throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.'); |
|
245 | + } |
|
246 | + |
|
247 | + /** |
|
248 | + * Offset to retrieve. |
|
249 | + * |
|
250 | + * @link http://php.net/manual/en/arrayaccess.offsetget.php |
|
251 | + * |
|
252 | + * @param mixed $offset <p> |
|
253 | + * The offset to retrieve. |
|
254 | + * </p> |
|
255 | + * |
|
256 | + * @return mixed Can return all value types |
|
257 | + * |
|
258 | + * @since 5.0.0 |
|
259 | + */ |
|
260 | + public function offsetGet($offset) |
|
261 | + { |
|
262 | + throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.'); |
|
263 | + } |
|
264 | + |
|
265 | + /** |
|
266 | + * Offset to set. |
|
267 | + * |
|
268 | + * @link http://php.net/manual/en/arrayaccess.offsetset.php |
|
269 | + * |
|
270 | + * @param mixed $offset <p> |
|
271 | + * The offset to assign the value to. |
|
272 | + * </p> |
|
273 | + * @param mixed $value <p> |
|
274 | + * The value to set. |
|
275 | + * </p> |
|
276 | + * |
|
277 | + * @since 5.0.0 |
|
278 | + */ |
|
279 | + public function offsetSet($offset, $value) |
|
280 | + { |
|
281 | + throw new TDBMInvalidOperationException('You can set values in a TDBM result set.'); |
|
282 | + } |
|
283 | + |
|
284 | + /** |
|
285 | + * Offset to unset. |
|
286 | + * |
|
287 | + * @link http://php.net/manual/en/arrayaccess.offsetunset.php |
|
288 | + * |
|
289 | + * @param mixed $offset <p> |
|
290 | + * The offset to unset. |
|
291 | + * </p> |
|
292 | + * |
|
293 | + * @since 5.0.0 |
|
294 | + */ |
|
295 | + public function offsetUnset($offset) |
|
296 | + { |
|
297 | + throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.'); |
|
298 | + } |
|
299 | 299 | } |
@@ -10,214 +10,214 @@ |
||
10 | 10 | |
11 | 11 | abstract class AbstractQueryFactory implements QueryFactory |
12 | 12 | { |
13 | - /** |
|
14 | - * @var TDBMService |
|
15 | - */ |
|
16 | - protected $tdbmService; |
|
17 | - |
|
18 | - /** |
|
19 | - * @var Schema |
|
20 | - */ |
|
21 | - protected $schema; |
|
22 | - |
|
23 | - /** |
|
24 | - * @var OrderByAnalyzer |
|
25 | - */ |
|
26 | - protected $orderByAnalyzer; |
|
27 | - |
|
28 | - /** |
|
29 | - * @var string|UncheckedOrderBy|null |
|
30 | - */ |
|
31 | - protected $orderBy; |
|
32 | - |
|
33 | - protected $magicSql; |
|
34 | - protected $magicSqlCount; |
|
35 | - protected $columnDescList; |
|
36 | - |
|
37 | - /** |
|
38 | - * @param TDBMService $tdbmService |
|
39 | - */ |
|
40 | - public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, $orderBy) |
|
41 | - { |
|
42 | - $this->tdbmService = $tdbmService; |
|
43 | - $this->schema = $schema; |
|
44 | - $this->orderByAnalyzer = $orderByAnalyzer; |
|
45 | - $this->orderBy = $orderBy; |
|
46 | - } |
|
47 | - |
|
48 | - /** |
|
49 | - * Returns the column list that must be fetched for the SQL request. |
|
50 | - * |
|
51 | - * Note: MySQL dictates that ORDER BYed columns should appear in the SELECT clause. |
|
52 | - * |
|
53 | - * @param string $mainTable |
|
54 | - * @param array $additionalTablesFetch |
|
55 | - * @param string|UncheckedOrderBy|null $orderBy |
|
56 | - * |
|
57 | - * @return array |
|
58 | - * |
|
59 | - * @throws \Doctrine\DBAL\Schema\SchemaException |
|
60 | - */ |
|
61 | - protected function getColumnsList(string $mainTable, array $additionalTablesFetch = array(), $orderBy = null) |
|
62 | - { |
|
63 | - // From the table name and the additional tables we want to fetch, let's build a list of all tables |
|
64 | - // that must be part of the select columns. |
|
65 | - |
|
66 | - $connection = $this->tdbmService->getConnection(); |
|
67 | - |
|
68 | - $tableGroups = []; |
|
69 | - $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($mainTable); |
|
70 | - $tableGroupName = $this->getTableGroupName($allFetchedTables); |
|
71 | - foreach ($allFetchedTables as $table) { |
|
72 | - $tableGroups[$table] = $tableGroupName; |
|
73 | - } |
|
74 | - |
|
75 | - $columnsList = []; |
|
76 | - $columnDescList = []; |
|
77 | - $sortColumn = 0; |
|
78 | - $reconstructedOrderBy = null; |
|
79 | - |
|
80 | - if (is_string($orderBy)) { |
|
81 | - $orderBy = trim($orderBy); |
|
82 | - if ($orderBy === '') { |
|
83 | - $orderBy = null; |
|
84 | - } |
|
85 | - } |
|
86 | - |
|
87 | - // Now, let's deal with "order by columns" |
|
88 | - if ($orderBy !== null) { |
|
89 | - if ($orderBy instanceof UncheckedOrderBy) { |
|
90 | - $securedOrderBy = false; |
|
91 | - $orderBy = $orderBy->getOrderBy(); |
|
92 | - $reconstructedOrderBy = $orderBy; |
|
93 | - } else { |
|
94 | - $securedOrderBy = true; |
|
95 | - $reconstructedOrderBys = []; |
|
96 | - } |
|
97 | - $orderByColumns = $this->orderByAnalyzer->analyzeOrderBy($orderBy); |
|
98 | - |
|
99 | - // If we sort by a column, there is a high chance we will fetch the bean containing this column. |
|
100 | - // Hence, we should add the table to the $additionalTablesFetch |
|
101 | - foreach ($orderByColumns as $orderByColumn) { |
|
102 | - if ($orderByColumn['type'] === 'colref') { |
|
103 | - if ($orderByColumn['table'] !== null) { |
|
104 | - $additionalTablesFetch[] = $orderByColumn['table']; |
|
105 | - } |
|
106 | - if ($securedOrderBy) { |
|
107 | - $reconstructedOrderBys[] = ($orderByColumn['table'] !== null ? $connection->quoteIdentifier($orderByColumn['table']).'.' : '').$connection->quoteIdentifier($orderByColumn['column']).' '.$orderByColumn['direction']; |
|
108 | - } |
|
109 | - } elseif ($orderByColumn['type'] === 'expr') { |
|
110 | - $sortColumnName = 'sort_column_'.$sortColumn; |
|
111 | - $columnsList[] = $orderByColumn['expr'].' as '.$sortColumnName; |
|
112 | - $columnDescList[$sortColumnName] = [ |
|
113 | - 'tableGroup' => null, |
|
114 | - ]; |
|
115 | - ++$sortColumn; |
|
116 | - |
|
117 | - if ($securedOrderBy) { |
|
118 | - throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")'); |
|
119 | - } |
|
120 | - } |
|
121 | - } |
|
122 | - |
|
123 | - if ($reconstructedOrderBy === null) { |
|
124 | - $reconstructedOrderBy = implode(', ', $reconstructedOrderBys); |
|
125 | - } |
|
126 | - } |
|
127 | - |
|
128 | - foreach ($additionalTablesFetch as $additionalTable) { |
|
129 | - $relatedTables = $this->tdbmService->_getRelatedTablesByInheritance($additionalTable); |
|
130 | - $tableGroupName = $this->getTableGroupName($relatedTables); |
|
131 | - foreach ($relatedTables as $table) { |
|
132 | - $tableGroups[$table] = $tableGroupName; |
|
133 | - } |
|
134 | - $allFetchedTables = array_merge($allFetchedTables, $relatedTables); |
|
135 | - } |
|
136 | - |
|
137 | - // Let's remove any duplicate |
|
138 | - $allFetchedTables = array_flip(array_flip($allFetchedTables)); |
|
139 | - |
|
140 | - // Now, let's build the column list |
|
141 | - foreach ($allFetchedTables as $table) { |
|
142 | - foreach ($this->schema->getTable($table)->getColumns() as $column) { |
|
143 | - $columnName = $column->getName(); |
|
144 | - $columnDescList[$table.'____'.$columnName] = [ |
|
145 | - 'as' => $table.'____'.$columnName, |
|
146 | - 'table' => $table, |
|
147 | - 'column' => $columnName, |
|
148 | - 'type' => $column->getType(), |
|
149 | - 'tableGroup' => $tableGroups[$table], |
|
150 | - ]; |
|
151 | - $columnsList[] = $connection->quoteIdentifier($table).'.'.$connection->quoteIdentifier($columnName).' as '. |
|
152 | - $connection->quoteIdentifier($table.'____'.$columnName); |
|
153 | - } |
|
154 | - } |
|
155 | - |
|
156 | - return [$columnDescList, $columnsList, $reconstructedOrderBy]; |
|
157 | - } |
|
158 | - |
|
159 | - abstract protected function compute(); |
|
160 | - |
|
161 | - /** |
|
162 | - * Returns an identifier for the group of tables passed in parameter. |
|
163 | - * |
|
164 | - * @param string[] $relatedTables |
|
165 | - * |
|
166 | - * @return string |
|
167 | - */ |
|
168 | - protected function getTableGroupName(array $relatedTables) |
|
169 | - { |
|
170 | - sort($relatedTables); |
|
171 | - |
|
172 | - return implode('_``_', $relatedTables); |
|
173 | - } |
|
174 | - |
|
175 | - public function getMagicSql() : string |
|
176 | - { |
|
177 | - if ($this->magicSql === null) { |
|
178 | - $this->compute(); |
|
179 | - } |
|
180 | - |
|
181 | - return $this->magicSql; |
|
182 | - } |
|
183 | - |
|
184 | - public function getMagicSqlCount() : string |
|
185 | - { |
|
186 | - if ($this->magicSqlCount === null) { |
|
187 | - $this->compute(); |
|
188 | - } |
|
189 | - |
|
190 | - return $this->magicSqlCount; |
|
191 | - } |
|
192 | - |
|
193 | - public function getColumnDescriptors() : array |
|
194 | - { |
|
195 | - if ($this->columnDescList === null) { |
|
196 | - $this->compute(); |
|
197 | - } |
|
198 | - |
|
199 | - return $this->columnDescList; |
|
200 | - } |
|
201 | - |
|
202 | - /** |
|
203 | - * Sets the ORDER BY directive executed in SQL. |
|
204 | - * |
|
205 | - * For instance: |
|
206 | - * |
|
207 | - * $queryFactory->sort('label ASC, status DESC'); |
|
208 | - * |
|
209 | - * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter. |
|
210 | - * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter: |
|
211 | - * |
|
212 | - * $queryFactory->sort(new UncheckedOrderBy('RAND()')) |
|
213 | - * |
|
214 | - * @param string|UncheckedOrderBy|null $orderBy |
|
215 | - */ |
|
216 | - public function sort($orderBy) |
|
217 | - { |
|
218 | - $this->orderBy = $orderBy; |
|
219 | - $this->magicSql = null; |
|
220 | - $this->magicSqlCount = null; |
|
221 | - $this->columnDescList = null; |
|
222 | - } |
|
13 | + /** |
|
14 | + * @var TDBMService |
|
15 | + */ |
|
16 | + protected $tdbmService; |
|
17 | + |
|
18 | + /** |
|
19 | + * @var Schema |
|
20 | + */ |
|
21 | + protected $schema; |
|
22 | + |
|
23 | + /** |
|
24 | + * @var OrderByAnalyzer |
|
25 | + */ |
|
26 | + protected $orderByAnalyzer; |
|
27 | + |
|
28 | + /** |
|
29 | + * @var string|UncheckedOrderBy|null |
|
30 | + */ |
|
31 | + protected $orderBy; |
|
32 | + |
|
33 | + protected $magicSql; |
|
34 | + protected $magicSqlCount; |
|
35 | + protected $columnDescList; |
|
36 | + |
|
37 | + /** |
|
38 | + * @param TDBMService $tdbmService |
|
39 | + */ |
|
40 | + public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, $orderBy) |
|
41 | + { |
|
42 | + $this->tdbmService = $tdbmService; |
|
43 | + $this->schema = $schema; |
|
44 | + $this->orderByAnalyzer = $orderByAnalyzer; |
|
45 | + $this->orderBy = $orderBy; |
|
46 | + } |
|
47 | + |
|
48 | + /** |
|
49 | + * Returns the column list that must be fetched for the SQL request. |
|
50 | + * |
|
51 | + * Note: MySQL dictates that ORDER BYed columns should appear in the SELECT clause. |
|
52 | + * |
|
53 | + * @param string $mainTable |
|
54 | + * @param array $additionalTablesFetch |
|
55 | + * @param string|UncheckedOrderBy|null $orderBy |
|
56 | + * |
|
57 | + * @return array |
|
58 | + * |
|
59 | + * @throws \Doctrine\DBAL\Schema\SchemaException |
|
60 | + */ |
|
61 | + protected function getColumnsList(string $mainTable, array $additionalTablesFetch = array(), $orderBy = null) |
|
62 | + { |
|
63 | + // From the table name and the additional tables we want to fetch, let's build a list of all tables |
|
64 | + // that must be part of the select columns. |
|
65 | + |
|
66 | + $connection = $this->tdbmService->getConnection(); |
|
67 | + |
|
68 | + $tableGroups = []; |
|
69 | + $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($mainTable); |
|
70 | + $tableGroupName = $this->getTableGroupName($allFetchedTables); |
|
71 | + foreach ($allFetchedTables as $table) { |
|
72 | + $tableGroups[$table] = $tableGroupName; |
|
73 | + } |
|
74 | + |
|
75 | + $columnsList = []; |
|
76 | + $columnDescList = []; |
|
77 | + $sortColumn = 0; |
|
78 | + $reconstructedOrderBy = null; |
|
79 | + |
|
80 | + if (is_string($orderBy)) { |
|
81 | + $orderBy = trim($orderBy); |
|
82 | + if ($orderBy === '') { |
|
83 | + $orderBy = null; |
|
84 | + } |
|
85 | + } |
|
86 | + |
|
87 | + // Now, let's deal with "order by columns" |
|
88 | + if ($orderBy !== null) { |
|
89 | + if ($orderBy instanceof UncheckedOrderBy) { |
|
90 | + $securedOrderBy = false; |
|
91 | + $orderBy = $orderBy->getOrderBy(); |
|
92 | + $reconstructedOrderBy = $orderBy; |
|
93 | + } else { |
|
94 | + $securedOrderBy = true; |
|
95 | + $reconstructedOrderBys = []; |
|
96 | + } |
|
97 | + $orderByColumns = $this->orderByAnalyzer->analyzeOrderBy($orderBy); |
|
98 | + |
|
99 | + // If we sort by a column, there is a high chance we will fetch the bean containing this column. |
|
100 | + // Hence, we should add the table to the $additionalTablesFetch |
|
101 | + foreach ($orderByColumns as $orderByColumn) { |
|
102 | + if ($orderByColumn['type'] === 'colref') { |
|
103 | + if ($orderByColumn['table'] !== null) { |
|
104 | + $additionalTablesFetch[] = $orderByColumn['table']; |
|
105 | + } |
|
106 | + if ($securedOrderBy) { |
|
107 | + $reconstructedOrderBys[] = ($orderByColumn['table'] !== null ? $connection->quoteIdentifier($orderByColumn['table']).'.' : '').$connection->quoteIdentifier($orderByColumn['column']).' '.$orderByColumn['direction']; |
|
108 | + } |
|
109 | + } elseif ($orderByColumn['type'] === 'expr') { |
|
110 | + $sortColumnName = 'sort_column_'.$sortColumn; |
|
111 | + $columnsList[] = $orderByColumn['expr'].' as '.$sortColumnName; |
|
112 | + $columnDescList[$sortColumnName] = [ |
|
113 | + 'tableGroup' => null, |
|
114 | + ]; |
|
115 | + ++$sortColumn; |
|
116 | + |
|
117 | + if ($securedOrderBy) { |
|
118 | + throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")'); |
|
119 | + } |
|
120 | + } |
|
121 | + } |
|
122 | + |
|
123 | + if ($reconstructedOrderBy === null) { |
|
124 | + $reconstructedOrderBy = implode(', ', $reconstructedOrderBys); |
|
125 | + } |
|
126 | + } |
|
127 | + |
|
128 | + foreach ($additionalTablesFetch as $additionalTable) { |
|
129 | + $relatedTables = $this->tdbmService->_getRelatedTablesByInheritance($additionalTable); |
|
130 | + $tableGroupName = $this->getTableGroupName($relatedTables); |
|
131 | + foreach ($relatedTables as $table) { |
|
132 | + $tableGroups[$table] = $tableGroupName; |
|
133 | + } |
|
134 | + $allFetchedTables = array_merge($allFetchedTables, $relatedTables); |
|
135 | + } |
|
136 | + |
|
137 | + // Let's remove any duplicate |
|
138 | + $allFetchedTables = array_flip(array_flip($allFetchedTables)); |
|
139 | + |
|
140 | + // Now, let's build the column list |
|
141 | + foreach ($allFetchedTables as $table) { |
|
142 | + foreach ($this->schema->getTable($table)->getColumns() as $column) { |
|
143 | + $columnName = $column->getName(); |
|
144 | + $columnDescList[$table.'____'.$columnName] = [ |
|
145 | + 'as' => $table.'____'.$columnName, |
|
146 | + 'table' => $table, |
|
147 | + 'column' => $columnName, |
|
148 | + 'type' => $column->getType(), |
|
149 | + 'tableGroup' => $tableGroups[$table], |
|
150 | + ]; |
|
151 | + $columnsList[] = $connection->quoteIdentifier($table).'.'.$connection->quoteIdentifier($columnName).' as '. |
|
152 | + $connection->quoteIdentifier($table.'____'.$columnName); |
|
153 | + } |
|
154 | + } |
|
155 | + |
|
156 | + return [$columnDescList, $columnsList, $reconstructedOrderBy]; |
|
157 | + } |
|
158 | + |
|
159 | + abstract protected function compute(); |
|
160 | + |
|
161 | + /** |
|
162 | + * Returns an identifier for the group of tables passed in parameter. |
|
163 | + * |
|
164 | + * @param string[] $relatedTables |
|
165 | + * |
|
166 | + * @return string |
|
167 | + */ |
|
168 | + protected function getTableGroupName(array $relatedTables) |
|
169 | + { |
|
170 | + sort($relatedTables); |
|
171 | + |
|
172 | + return implode('_``_', $relatedTables); |
|
173 | + } |
|
174 | + |
|
175 | + public function getMagicSql() : string |
|
176 | + { |
|
177 | + if ($this->magicSql === null) { |
|
178 | + $this->compute(); |
|
179 | + } |
|
180 | + |
|
181 | + return $this->magicSql; |
|
182 | + } |
|
183 | + |
|
184 | + public function getMagicSqlCount() : string |
|
185 | + { |
|
186 | + if ($this->magicSqlCount === null) { |
|
187 | + $this->compute(); |
|
188 | + } |
|
189 | + |
|
190 | + return $this->magicSqlCount; |
|
191 | + } |
|
192 | + |
|
193 | + public function getColumnDescriptors() : array |
|
194 | + { |
|
195 | + if ($this->columnDescList === null) { |
|
196 | + $this->compute(); |
|
197 | + } |
|
198 | + |
|
199 | + return $this->columnDescList; |
|
200 | + } |
|
201 | + |
|
202 | + /** |
|
203 | + * Sets the ORDER BY directive executed in SQL. |
|
204 | + * |
|
205 | + * For instance: |
|
206 | + * |
|
207 | + * $queryFactory->sort('label ASC, status DESC'); |
|
208 | + * |
|
209 | + * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter. |
|
210 | + * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter: |
|
211 | + * |
|
212 | + * $queryFactory->sort(new UncheckedOrderBy('RAND()')) |
|
213 | + * |
|
214 | + * @param string|UncheckedOrderBy|null $orderBy |
|
215 | + */ |
|
216 | + public function sort($orderBy) |
|
217 | + { |
|
218 | + $this->orderBy = $orderBy; |
|
219 | + $this->magicSql = null; |
|
220 | + $this->magicSqlCount = null; |
|
221 | + $this->columnDescList = null; |
|
222 | + } |
|
223 | 223 | } |