Passed
Push — 4.3 ( 28cdbc...a1e8cd )
by David
14:50 queued 13:41
created
src/Mouf/Database/TDBM/Commands/AlteredConfiguration.php 1 patch
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -20,103 +20,103 @@
 block discarded – undo
20 20
  */
21 21
 class AlteredConfiguration implements ConfigurationInterface
22 22
 {
23
-    /**
24
-     * @var ConfigurationInterface
25
-     */
26
-    private $configuration;
27
-
28
-    /**
29
-     * @var LoggerInterface
30
-     */
31
-    private $logger;
32
-
33
-    public function __construct(ConfigurationInterface $configuration)
34
-    {
35
-        $this->configuration = $configuration;
36
-        $this->logger = $configuration->getLogger();
37
-    }
38
-
39
-
40
-    /**
41
-     * @return string
42
-     */
43
-    public function getBeanNamespace(): string
44
-    {
45
-        return $this->configuration->getBeanNamespace();
46
-    }
47
-
48
-    /**
49
-     * @return string
50
-     */
51
-    public function getDaoNamespace(): string
52
-    {
53
-        return $this->configuration->getDaoNamespace();
54
-    }
55
-
56
-    /**
57
-     * @return Connection
58
-     */
59
-    public function getConnection(): Connection
60
-    {
61
-        return $this->configuration->getConnection();
62
-    }
63
-
64
-    /**
65
-     * @return Cache
66
-     */
67
-    public function getCache(): Cache
68
-    {
69
-        return $this->configuration->getCache();
70
-    }
71
-
72
-    /**
73
-     * @return NamingStrategyInterface
74
-     */
75
-    public function getNamingStrategy(): NamingStrategyInterface
76
-    {
77
-        return $this->configuration->getNamingStrategy();
78
-    }
79
-
80
-    /**
81
-     * @return SchemaAnalyzer
82
-     */
83
-    public function getSchemaAnalyzer(): SchemaAnalyzer
84
-    {
85
-        return $this->configuration->getSchemaAnalyzer();
86
-    }
87
-
88
-    /**
89
-     * @return LoggerInterface
90
-     */
91
-    public function getLogger(): ?LoggerInterface
92
-    {
93
-        return $this->logger;
94
-    }
95
-
96
-    /**
97
-     * @return GeneratorListenerInterface
98
-     */
99
-    public function getGeneratorEventDispatcher(): GeneratorListenerInterface
100
-    {
101
-        return $this->configuration->getGeneratorEventDispatcher();
102
-    }
103
-
104
-    /**
105
-     * @param LoggerInterface $logger
106
-     */
107
-    public function setLogger(LoggerInterface $logger)
108
-    {
109
-        $this->logger = $logger;
110
-    }
111
-
112
-    /**
113
-     * Returns a class able to find the place of a PHP file based on the class name.
114
-     * Useful to find the path where DAOs and beans should be written to.
115
-     *
116
-     * @return PathFinderInterface
117
-     */
118
-    public function getPathFinder(): PathFinderInterface
119
-    {
120
-        return $this->configuration->getPathFinder();
121
-    }
23
+	/**
24
+	 * @var ConfigurationInterface
25
+	 */
26
+	private $configuration;
27
+
28
+	/**
29
+	 * @var LoggerInterface
30
+	 */
31
+	private $logger;
32
+
33
+	public function __construct(ConfigurationInterface $configuration)
34
+	{
35
+		$this->configuration = $configuration;
36
+		$this->logger = $configuration->getLogger();
37
+	}
38
+
39
+
40
+	/**
41
+	 * @return string
42
+	 */
43
+	public function getBeanNamespace(): string
44
+	{
45
+		return $this->configuration->getBeanNamespace();
46
+	}
47
+
48
+	/**
49
+	 * @return string
50
+	 */
51
+	public function getDaoNamespace(): string
52
+	{
53
+		return $this->configuration->getDaoNamespace();
54
+	}
55
+
56
+	/**
57
+	 * @return Connection
58
+	 */
59
+	public function getConnection(): Connection
60
+	{
61
+		return $this->configuration->getConnection();
62
+	}
63
+
64
+	/**
65
+	 * @return Cache
66
+	 */
67
+	public function getCache(): Cache
68
+	{
69
+		return $this->configuration->getCache();
70
+	}
71
+
72
+	/**
73
+	 * @return NamingStrategyInterface
74
+	 */
75
+	public function getNamingStrategy(): NamingStrategyInterface
76
+	{
77
+		return $this->configuration->getNamingStrategy();
78
+	}
79
+
80
+	/**
81
+	 * @return SchemaAnalyzer
82
+	 */
83
+	public function getSchemaAnalyzer(): SchemaAnalyzer
84
+	{
85
+		return $this->configuration->getSchemaAnalyzer();
86
+	}
87
+
88
+	/**
89
+	 * @return LoggerInterface
90
+	 */
91
+	public function getLogger(): ?LoggerInterface
92
+	{
93
+		return $this->logger;
94
+	}
95
+
96
+	/**
97
+	 * @return GeneratorListenerInterface
98
+	 */
99
+	public function getGeneratorEventDispatcher(): GeneratorListenerInterface
100
+	{
101
+		return $this->configuration->getGeneratorEventDispatcher();
102
+	}
103
+
104
+	/**
105
+	 * @param LoggerInterface $logger
106
+	 */
107
+	public function setLogger(LoggerInterface $logger)
108
+	{
109
+		$this->logger = $logger;
110
+	}
111
+
112
+	/**
113
+	 * Returns a class able to find the place of a PHP file based on the class name.
114
+	 * Useful to find the path where DAOs and beans should be written to.
115
+	 *
116
+	 * @return PathFinderInterface
117
+	 */
118
+	public function getPathFinder(): PathFinderInterface
119
+	{
120
+		return $this->configuration->getPathFinder();
121
+	}
122 122
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Configuration.php 1 patch
Indentation   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -17,165 +17,165 @@
 block discarded – undo
17 17
 class Configuration implements ConfigurationInterface
18 18
 {
19 19
 
20
-    /**
21
-     * @var string
22
-     */
23
-    private $beanNamespace;
24
-    /**
25
-     * @var string
26
-     */
27
-    private $daoNamespace;
28
-    /**
29
-     * @var Connection
30
-     */
31
-    private $connection;
32
-    /**
33
-     * @var Cache
34
-     */
35
-    private $cache;
36
-    /**
37
-     * @var SchemaAnalyzer
38
-     */
39
-    private $schemaAnalyzer;
40
-    /**
41
-     * @var LoggerInterface
42
-     */
43
-    private $logger;
44
-    /**
45
-     * @var GeneratorListenerInterface
46
-     */
47
-    private $generatorEventDispatcher;
48
-    /**
49
-     * @var NamingStrategyInterface
50
-     */
51
-    private $namingStrategy;
52
-    /**
53
-     * @var PathFinderInterface
54
-     */
55
-    private $pathFinder;
56
-
57
-    /**
58
-     * @param string $beanNamespace The namespace hosting the beans
59
-     * @param string $daoNamespace The namespace hosting the DAOs
60
-     * @param Connection $connection The connection to the database
61
-     * @param Cache|null $cache The Doctrine cache to store database metadata
62
-     * @param SchemaAnalyzer|null $schemaAnalyzer The schema analyzer that will be used to find shortest paths... Will be automatically created if not passed
63
-     * @param LoggerInterface|null $logger The logger
64
-     * @param GeneratorListenerInterface[] $generatorListeners A list of listeners that will be triggered when beans/daos are generated
65
-     */
66
-    public function __construct(string $beanNamespace, string $daoNamespace, Connection $connection, NamingStrategyInterface $namingStrategy, Cache $cache = null, SchemaAnalyzer $schemaAnalyzer = null, LoggerInterface $logger = null, array $generatorListeners = [])
67
-    {
68
-        $this->beanNamespace = rtrim($beanNamespace, '\\');
69
-        $this->daoNamespace = rtrim($daoNamespace, '\\');
70
-        $this->connection = $connection;
71
-        $this->namingStrategy = $namingStrategy;
72
-        if ($cache !== null) {
73
-            $this->cache = $cache;
74
-        } else {
75
-            $this->cache = new VoidCache();
76
-        }
77
-        if ($schemaAnalyzer !== null) {
78
-            $this->schemaAnalyzer = $schemaAnalyzer;
79
-        } else {
80
-            $this->schemaAnalyzer = new SchemaAnalyzer($this->connection->getSchemaManager(), $this->cache, $this->getConnectionUniqueId());
81
-        }
82
-        $this->logger = $logger;
83
-        $this->generatorEventDispatcher = new GeneratorEventDispatcher($generatorListeners);
84
-        $this->pathFinder = new PathFinder();
85
-    }
86
-
87
-    /**
88
-     * @return string
89
-     */
90
-    public function getBeanNamespace(): string
91
-    {
92
-        return $this->beanNamespace;
93
-    }
94
-
95
-    /**
96
-     * @return string
97
-     */
98
-    public function getDaoNamespace(): string
99
-    {
100
-        return $this->daoNamespace;
101
-    }
102
-
103
-    /**
104
-     * @return Connection
105
-     */
106
-    public function getConnection(): Connection
107
-    {
108
-        return $this->connection;
109
-    }
110
-
111
-    /**
112
-     * @return NamingStrategyInterface
113
-     */
114
-    public function getNamingStrategy(): NamingStrategyInterface
115
-    {
116
-        return $this->namingStrategy;
117
-    }
118
-
119
-    /**
120
-     * @return Cache
121
-     */
122
-    public function getCache(): Cache
123
-    {
124
-        return $this->cache;
125
-    }
126
-
127
-    /**
128
-     * @return SchemaAnalyzer
129
-     */
130
-    public function getSchemaAnalyzer(): SchemaAnalyzer
131
-    {
132
-        return $this->schemaAnalyzer;
133
-    }
134
-
135
-    /**
136
-     * @return LoggerInterface
137
-     */
138
-    public function getLogger(): ?LoggerInterface
139
-    {
140
-        return $this->logger;
141
-    }
142
-
143
-    /**
144
-     * @return GeneratorListenerInterface
145
-     */
146
-    public function getGeneratorEventDispatcher(): GeneratorListenerInterface
147
-    {
148
-        return $this->generatorEventDispatcher;
149
-    }
150
-
151
-
152
-
153
-    /**
154
-     * Creates a unique cache key for the current connection.
155
-     *
156
-     * @return string
157
-     */
158
-    private function getConnectionUniqueId(): string
159
-    {
160
-        return hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
161
-    }
162
-
163
-    /**
164
-     * Returns a class able to find the place of a PHP file based on the class name.
165
-     * Useful to find the path where DAOs and beans should be written to.
166
-     *
167
-     * @return PathFinderInterface
168
-     */
169
-    public function getPathFinder(): PathFinderInterface
170
-    {
171
-        return $this->pathFinder;
172
-    }
173
-
174
-    /**
175
-     * @param PathFinderInterface $pathFinder
176
-     */
177
-    public function setPathFinder(PathFinderInterface $pathFinder)
178
-    {
179
-        $this->pathFinder = $pathFinder;
180
-    }
20
+	/**
21
+	 * @var string
22
+	 */
23
+	private $beanNamespace;
24
+	/**
25
+	 * @var string
26
+	 */
27
+	private $daoNamespace;
28
+	/**
29
+	 * @var Connection
30
+	 */
31
+	private $connection;
32
+	/**
33
+	 * @var Cache
34
+	 */
35
+	private $cache;
36
+	/**
37
+	 * @var SchemaAnalyzer
38
+	 */
39
+	private $schemaAnalyzer;
40
+	/**
41
+	 * @var LoggerInterface
42
+	 */
43
+	private $logger;
44
+	/**
45
+	 * @var GeneratorListenerInterface
46
+	 */
47
+	private $generatorEventDispatcher;
48
+	/**
49
+	 * @var NamingStrategyInterface
50
+	 */
51
+	private $namingStrategy;
52
+	/**
53
+	 * @var PathFinderInterface
54
+	 */
55
+	private $pathFinder;
56
+
57
+	/**
58
+	 * @param string $beanNamespace The namespace hosting the beans
59
+	 * @param string $daoNamespace The namespace hosting the DAOs
60
+	 * @param Connection $connection The connection to the database
61
+	 * @param Cache|null $cache The Doctrine cache to store database metadata
62
+	 * @param SchemaAnalyzer|null $schemaAnalyzer The schema analyzer that will be used to find shortest paths... Will be automatically created if not passed
63
+	 * @param LoggerInterface|null $logger The logger
64
+	 * @param GeneratorListenerInterface[] $generatorListeners A list of listeners that will be triggered when beans/daos are generated
65
+	 */
66
+	public function __construct(string $beanNamespace, string $daoNamespace, Connection $connection, NamingStrategyInterface $namingStrategy, Cache $cache = null, SchemaAnalyzer $schemaAnalyzer = null, LoggerInterface $logger = null, array $generatorListeners = [])
67
+	{
68
+		$this->beanNamespace = rtrim($beanNamespace, '\\');
69
+		$this->daoNamespace = rtrim($daoNamespace, '\\');
70
+		$this->connection = $connection;
71
+		$this->namingStrategy = $namingStrategy;
72
+		if ($cache !== null) {
73
+			$this->cache = $cache;
74
+		} else {
75
+			$this->cache = new VoidCache();
76
+		}
77
+		if ($schemaAnalyzer !== null) {
78
+			$this->schemaAnalyzer = $schemaAnalyzer;
79
+		} else {
80
+			$this->schemaAnalyzer = new SchemaAnalyzer($this->connection->getSchemaManager(), $this->cache, $this->getConnectionUniqueId());
81
+		}
82
+		$this->logger = $logger;
83
+		$this->generatorEventDispatcher = new GeneratorEventDispatcher($generatorListeners);
84
+		$this->pathFinder = new PathFinder();
85
+	}
86
+
87
+	/**
88
+	 * @return string
89
+	 */
90
+	public function getBeanNamespace(): string
91
+	{
92
+		return $this->beanNamespace;
93
+	}
94
+
95
+	/**
96
+	 * @return string
97
+	 */
98
+	public function getDaoNamespace(): string
99
+	{
100
+		return $this->daoNamespace;
101
+	}
102
+
103
+	/**
104
+	 * @return Connection
105
+	 */
106
+	public function getConnection(): Connection
107
+	{
108
+		return $this->connection;
109
+	}
110
+
111
+	/**
112
+	 * @return NamingStrategyInterface
113
+	 */
114
+	public function getNamingStrategy(): NamingStrategyInterface
115
+	{
116
+		return $this->namingStrategy;
117
+	}
118
+
119
+	/**
120
+	 * @return Cache
121
+	 */
122
+	public function getCache(): Cache
123
+	{
124
+		return $this->cache;
125
+	}
126
+
127
+	/**
128
+	 * @return SchemaAnalyzer
129
+	 */
130
+	public function getSchemaAnalyzer(): SchemaAnalyzer
131
+	{
132
+		return $this->schemaAnalyzer;
133
+	}
134
+
135
+	/**
136
+	 * @return LoggerInterface
137
+	 */
138
+	public function getLogger(): ?LoggerInterface
139
+	{
140
+		return $this->logger;
141
+	}
142
+
143
+	/**
144
+	 * @return GeneratorListenerInterface
145
+	 */
146
+	public function getGeneratorEventDispatcher(): GeneratorListenerInterface
147
+	{
148
+		return $this->generatorEventDispatcher;
149
+	}
150
+
151
+
152
+
153
+	/**
154
+	 * Creates a unique cache key for the current connection.
155
+	 *
156
+	 * @return string
157
+	 */
158
+	private function getConnectionUniqueId(): string
159
+	{
160
+		return hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
161
+	}
162
+
163
+	/**
164
+	 * Returns a class able to find the place of a PHP file based on the class name.
165
+	 * Useful to find the path where DAOs and beans should be written to.
166
+	 *
167
+	 * @return PathFinderInterface
168
+	 */
169
+	public function getPathFinder(): PathFinderInterface
170
+	{
171
+		return $this->pathFinder;
172
+	}
173
+
174
+	/**
175
+	 * @param PathFinderInterface $pathFinder
176
+	 */
177
+	public function setPathFinder(PathFinderInterface $pathFinder)
178
+	{
179
+		$this->pathFinder = $pathFinder;
180
+	}
181 181
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Commands/GenerateCommand.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -12,48 +12,48 @@
 block discarded – undo
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
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/TDBMService.php 1 patch
Indentation   +1358 added lines, -1358 removed lines patch added patch discarded remove patch
@@ -48,384 +48,384 @@  discard block
 block discarded – undo
48 48
  */
49 49
 class TDBMService
50 50
 {
51
-    const MODE_CURSOR = 1;
52
-    const MODE_ARRAY = 2;
53
-
54
-    /**
55
-     * The database connection.
56
-     *
57
-     * @var Connection
58
-     */
59
-    private $connection;
60
-
61
-    /**
62
-     * @var SchemaAnalyzer
63
-     */
64
-    private $schemaAnalyzer;
65
-
66
-    /**
67
-     * @var MagicQuery
68
-     */
69
-    private $magicQuery;
70
-
71
-    /**
72
-     * @var TDBMSchemaAnalyzer
73
-     */
74
-    private $tdbmSchemaAnalyzer;
75
-
76
-    /**
77
-     * @var string
78
-     */
79
-    private $cachePrefix;
80
-
81
-    /**
82
-     * Cache of table of primary keys.
83
-     * Primary keys are stored by tables, as an array of column.
84
-     * For instance $primary_key['my_table'][0] will return the first column of the primary key of table 'my_table'.
85
-     *
86
-     * @var string[]
87
-     */
88
-    private $primaryKeysColumns;
89
-
90
-    /**
91
-     * Service storing objects in memory.
92
-     * Access is done by table name and then by primary key.
93
-     * If the primary key is split on several columns, access is done by an array of columns, serialized.
94
-     *
95
-     * @var StandardObjectStorage|WeakrefObjectStorage
96
-     */
97
-    private $objectStorage;
98
-
99
-    /**
100
-     * The fetch mode of the result sets returned by `getObjects`.
101
-     * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY or TDBMObjectArray::MODE_COMPATIBLE_ARRAY.
102
-     *
103
-     * In 'MODE_ARRAY' mode (default), the result is an array. Use this mode by default (unless the list returned is very big).
104
-     * In 'MODE_CURSOR' mode, the result is a Generator which is an iterable collection that can be scanned only once (only one "foreach") on it,
105
-     * and it cannot be accessed via key. Use this mode for large datasets processed by batch.
106
-     * In 'MODE_COMPATIBLE_ARRAY' mode, the result is an old TDBMObjectArray (used up to TDBM 3.2).
107
-     * You can access the array by key, or using foreach, several times.
108
-     *
109
-     * @var int
110
-     */
111
-    private $mode = self::MODE_ARRAY;
112
-
113
-    /**
114
-     * Table of new objects not yet inserted in database or objects modified that must be saved.
115
-     *
116
-     * @var \SplObjectStorage of DbRow objects
117
-     */
118
-    private $toSaveObjects;
119
-
120
-    /**
121
-     * A cache service to be used.
122
-     *
123
-     * @var Cache|null
124
-     */
125
-    private $cache;
126
-
127
-    /**
128
-     * Map associating a table name to a fully qualified Bean class name.
129
-     *
130
-     * @var array
131
-     */
132
-    private $tableToBeanMap = [];
133
-
134
-    /**
135
-     * @var \ReflectionClass[]
136
-     */
137
-    private $reflectionClassCache = array();
138
-
139
-    /**
140
-     * @var LoggerInterface
141
-     */
142
-    private $rootLogger;
143
-
144
-    /**
145
-     * @var LevelFilter|NullLogger
146
-     */
147
-    private $logger;
148
-
149
-    /**
150
-     * @var OrderByAnalyzer
151
-     */
152
-    private $orderByAnalyzer;
153
-
154
-    /**
155
-     * @var string
156
-     */
157
-    private $beanNamespace;
158
-
159
-    /**
160
-     * @var NamingStrategyInterface
161
-     */
162
-    private $namingStrategy;
163
-    /**
164
-     * @var ConfigurationInterface
165
-     */
166
-    private $configuration;
167
-
168
-    /**
169
-     * @param ConfigurationInterface $configuration The configuration object
170
-     */
171
-    public function __construct(ConfigurationInterface $configuration)
172
-    {
173
-        if (extension_loaded('weakref')) {
174
-            $this->objectStorage = new WeakrefObjectStorage();
175
-        } else {
176
-            $this->objectStorage = new StandardObjectStorage();
177
-        }
178
-        $this->connection = $configuration->getConnection();
179
-        $this->cache = $configuration->getCache();
180
-        $this->schemaAnalyzer = $configuration->getSchemaAnalyzer();
181
-
182
-        $this->magicQuery = new MagicQuery($this->connection, $this->cache, $this->schemaAnalyzer);
183
-
184
-        $this->tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->connection, $this->cache, $this->schemaAnalyzer);
185
-        $this->cachePrefix = $this->tdbmSchemaAnalyzer->getCachePrefix();
186
-
187
-        $this->toSaveObjects = new \SplObjectStorage();
188
-        $logger = $configuration->getLogger();
189
-        if ($logger === null) {
190
-            $this->logger = new NullLogger();
191
-            $this->rootLogger = new NullLogger();
192
-        } else {
193
-            $this->rootLogger = $logger;
194
-            $this->setLogLevel(LogLevel::WARNING);
195
-        }
196
-        $this->orderByAnalyzer = new OrderByAnalyzer($this->cache, $this->cachePrefix);
197
-        $this->beanNamespace = $configuration->getBeanNamespace();
198
-        $this->namingStrategy = $configuration->getNamingStrategy();
199
-        $this->configuration = $configuration;
200
-    }
201
-
202
-    /**
203
-     * Returns the object used to connect to the database.
204
-     *
205
-     * @return Connection
206
-     */
207
-    public function getConnection(): Connection
208
-    {
209
-        return $this->connection;
210
-    }
211
-
212
-    /**
213
-     * Sets the default fetch mode of the result sets returned by `findObjects`.
214
-     * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY.
215
-     *
216
-     * In 'MODE_ARRAY' mode (default), the result is a ResultIterator object that behaves like an array. Use this mode by default (unless the list returned is very big).
217
-     * In 'MODE_CURSOR' mode, the result is a ResultIterator object. If you scan it many times (by calling several time a foreach loop), the query will be run
218
-     * several times. In cursor mode, you cannot access the result set by key. Use this mode for large datasets processed by batch.
219
-     *
220
-     * @param int $mode
221
-     *
222
-     * @return $this
223
-     *
224
-     * @throws TDBMException
225
-     */
226
-    public function setFetchMode($mode)
227
-    {
228
-        if ($mode !== self::MODE_CURSOR && $mode !== self::MODE_ARRAY) {
229
-            throw new TDBMException("Unknown fetch mode: '".$this->mode."'");
230
-        }
231
-        $this->mode = $mode;
232
-
233
-        return $this;
234
-    }
235
-
236
-    /**
237
-     * Removes the given object from database.
238
-     * This cannot be called on an object that is not attached to this TDBMService
239
-     * (will throw a TDBMInvalidOperationException).
240
-     *
241
-     * @param AbstractTDBMObject $object the object to delete
242
-     *
243
-     * @throws TDBMException
244
-     * @throws TDBMInvalidOperationException
245
-     */
246
-    public function delete(AbstractTDBMObject $object)
247
-    {
248
-        switch ($object->_getStatus()) {
249
-            case TDBMObjectStateEnum::STATE_DELETED:
250
-                // Nothing to do, object already deleted.
251
-                return;
252
-            case TDBMObjectStateEnum::STATE_DETACHED:
253
-                throw new TDBMInvalidOperationException('Cannot delete a detached object');
254
-            case TDBMObjectStateEnum::STATE_NEW:
255
-                $this->deleteManyToManyRelationships($object);
256
-                foreach ($object->_getDbRows() as $dbRow) {
257
-                    $this->removeFromToSaveObjectList($dbRow);
258
-                }
259
-                break;
260
-            case TDBMObjectStateEnum::STATE_DIRTY:
261
-                foreach ($object->_getDbRows() as $dbRow) {
262
-                    $this->removeFromToSaveObjectList($dbRow);
263
-                }
264
-                // And continue deleting...
265
-            case TDBMObjectStateEnum::STATE_NOT_LOADED:
266
-            case TDBMObjectStateEnum::STATE_LOADED:
267
-                $this->deleteManyToManyRelationships($object);
268
-                // Let's delete db rows, in reverse order.
269
-                foreach (array_reverse($object->_getDbRows()) as $dbRow) {
270
-                    $tableName = $dbRow->_getDbTableName();
271
-                    $primaryKeys = $dbRow->_getPrimaryKeys();
272
-                    $this->connection->delete($tableName, $primaryKeys);
273
-                    $this->objectStorage->remove($dbRow->_getDbTableName(), $this->getObjectHash($primaryKeys));
274
-                }
275
-                break;
276
-            // @codeCoverageIgnoreStart
277
-            default:
278
-                throw new TDBMInvalidOperationException('Unexpected status for bean');
279
-            // @codeCoverageIgnoreEnd
280
-        }
281
-
282
-        $object->_setStatus(TDBMObjectStateEnum::STATE_DELETED);
283
-    }
284
-
285
-    /**
286
-     * Removes all many to many relationships for this object.
287
-     *
288
-     * @param AbstractTDBMObject $object
289
-     */
290
-    private function deleteManyToManyRelationships(AbstractTDBMObject $object)
291
-    {
292
-        foreach ($object->_getDbRows() as $tableName => $dbRow) {
293
-            $pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
294
-            foreach ($pivotTables as $pivotTable) {
295
-                $remoteBeans = $object->_getRelationships($pivotTable);
296
-                foreach ($remoteBeans as $remoteBean) {
297
-                    $object->_removeRelationship($pivotTable, $remoteBean);
298
-                }
299
-            }
300
-        }
301
-        $this->persistManyToManyRelationships($object);
302
-    }
303
-
304
-    /**
305
-     * This function removes the given object from the database. It will also remove all objects relied to the one given
306
-     * by parameter before all.
307
-     *
308
-     * Notice: if the object has a multiple primary key, the function will not work.
309
-     *
310
-     * @param AbstractTDBMObject $objToDelete
311
-     */
312
-    public function deleteCascade(AbstractTDBMObject $objToDelete)
313
-    {
314
-        $this->deleteAllConstraintWithThisObject($objToDelete);
315
-        $this->delete($objToDelete);
316
-    }
317
-
318
-    /**
319
-     * This function is used only in TDBMService (private function)
320
-     * It will call deleteCascade function foreach object relied with a foreign key to the object given by parameter.
321
-     *
322
-     * @param AbstractTDBMObject $obj
323
-     */
324
-    private function deleteAllConstraintWithThisObject(AbstractTDBMObject $obj)
325
-    {
326
-        $dbRows = $obj->_getDbRows();
327
-        foreach ($dbRows as $dbRow) {
328
-            $tableName = $dbRow->_getDbTableName();
329
-            $pks = array_values($dbRow->_getPrimaryKeys());
330
-            if (!empty($pks)) {
331
-                $incomingFks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($tableName);
332
-
333
-                foreach ($incomingFks as $incomingFk) {
334
-                    $filter = array_combine($incomingFk->getLocalColumns(), $pks);
335
-
336
-                    $results = $this->findObjects($incomingFk->getLocalTableName(), $filter);
337
-
338
-                    foreach ($results as $bean) {
339
-                        $this->deleteCascade($bean);
340
-                    }
341
-                }
342
-            }
343
-        }
344
-    }
345
-
346
-    /**
347
-     * This function performs a save() of all the objects that have been modified.
348
-     */
349
-    public function completeSave()
350
-    {
351
-        foreach ($this->toSaveObjects as $dbRow) {
352
-            $this->save($dbRow->getTDBMObject());
353
-        }
354
-    }
355
-
356
-    /**
357
-     * Takes in input a filter_bag (which can be about anything from a string to an array of TDBMObjects... see above from documentation),
358
-     * and gives back a proper Filter object.
359
-     *
360
-     * @param mixed $filter_bag
361
-     * @param int   $counter
362
-     *
363
-     * @return array First item: filter string, second item: parameters
364
-     *
365
-     * @throws TDBMException
366
-     */
367
-    public function buildFilterFromFilterBag($filter_bag, $counter = 1)
368
-    {
369
-        if ($filter_bag === null) {
370
-            return ['', []];
371
-        } elseif (is_string($filter_bag)) {
372
-            return [$filter_bag, []];
373
-        } elseif (is_array($filter_bag)) {
374
-            $sqlParts = [];
375
-            $parameters = [];
376
-            foreach ($filter_bag as $column => $value) {
377
-                if (is_int($column)) {
378
-                    list($subSqlPart, $subParameters) = $this->buildFilterFromFilterBag($value, $counter);
379
-                    $sqlParts[] = $subSqlPart;
380
-                    $parameters += $subParameters;
381
-                } else {
382
-                    $paramName = 'tdbmparam'.$counter;
383
-                    if (is_array($value)) {
384
-                        $sqlParts[] = $this->connection->quoteIdentifier($column).' IN :'.$paramName;
385
-                    } else {
386
-                        $sqlParts[] = $this->connection->quoteIdentifier($column).' = :'.$paramName;
387
-                    }
388
-                    $parameters[$paramName] = $value;
389
-                    ++$counter;
390
-                }
391
-            }
392
-
393
-            return [implode(' AND ', $sqlParts), $parameters];
394
-        } elseif ($filter_bag instanceof AbstractTDBMObject) {
395
-            $sqlParts = [];
396
-            $parameters = [];
397
-            $dbRows = $filter_bag->_getDbRows();
398
-            $dbRow = reset($dbRows);
399
-            $primaryKeys = $dbRow->_getPrimaryKeys();
400
-
401
-            foreach ($primaryKeys as $column => $value) {
402
-                $paramName = 'tdbmparam'.$counter;
403
-                $sqlParts[] = $this->connection->quoteIdentifier($dbRow->_getDbTableName()).'.'.$this->connection->quoteIdentifier($column).' = :'.$paramName;
404
-                $parameters[$paramName] = $value;
405
-                ++$counter;
406
-            }
407
-
408
-            return [implode(' AND ', $sqlParts), $parameters];
409
-        } elseif ($filter_bag instanceof \Iterator) {
410
-            return $this->buildFilterFromFilterBag(iterator_to_array($filter_bag), $counter);
411
-        } else {
412
-            throw new TDBMException('Error in filter. An object has been passed that is neither a SQL string, nor an array, nor a bean, nor null.');
413
-        }
414
-    }
415
-
416
-    /**
417
-     * @param string $table
418
-     *
419
-     * @return string[]
420
-     */
421
-    public function getPrimaryKeyColumns($table)
422
-    {
423
-        if (!isset($this->primaryKeysColumns[$table])) {
424
-            $this->primaryKeysColumns[$table] = $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getPrimaryKeyColumns();
425
-
426
-            // TODO TDBM4: See if we need to improve error reporting if table name does not exist.
427
-
428
-            /*$arr = array();
51
+	const MODE_CURSOR = 1;
52
+	const MODE_ARRAY = 2;
53
+
54
+	/**
55
+	 * The database connection.
56
+	 *
57
+	 * @var Connection
58
+	 */
59
+	private $connection;
60
+
61
+	/**
62
+	 * @var SchemaAnalyzer
63
+	 */
64
+	private $schemaAnalyzer;
65
+
66
+	/**
67
+	 * @var MagicQuery
68
+	 */
69
+	private $magicQuery;
70
+
71
+	/**
72
+	 * @var TDBMSchemaAnalyzer
73
+	 */
74
+	private $tdbmSchemaAnalyzer;
75
+
76
+	/**
77
+	 * @var string
78
+	 */
79
+	private $cachePrefix;
80
+
81
+	/**
82
+	 * Cache of table of primary keys.
83
+	 * Primary keys are stored by tables, as an array of column.
84
+	 * For instance $primary_key['my_table'][0] will return the first column of the primary key of table 'my_table'.
85
+	 *
86
+	 * @var string[]
87
+	 */
88
+	private $primaryKeysColumns;
89
+
90
+	/**
91
+	 * Service storing objects in memory.
92
+	 * Access is done by table name and then by primary key.
93
+	 * If the primary key is split on several columns, access is done by an array of columns, serialized.
94
+	 *
95
+	 * @var StandardObjectStorage|WeakrefObjectStorage
96
+	 */
97
+	private $objectStorage;
98
+
99
+	/**
100
+	 * The fetch mode of the result sets returned by `getObjects`.
101
+	 * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY or TDBMObjectArray::MODE_COMPATIBLE_ARRAY.
102
+	 *
103
+	 * In 'MODE_ARRAY' mode (default), the result is an array. Use this mode by default (unless the list returned is very big).
104
+	 * In 'MODE_CURSOR' mode, the result is a Generator which is an iterable collection that can be scanned only once (only one "foreach") on it,
105
+	 * and it cannot be accessed via key. Use this mode for large datasets processed by batch.
106
+	 * In 'MODE_COMPATIBLE_ARRAY' mode, the result is an old TDBMObjectArray (used up to TDBM 3.2).
107
+	 * You can access the array by key, or using foreach, several times.
108
+	 *
109
+	 * @var int
110
+	 */
111
+	private $mode = self::MODE_ARRAY;
112
+
113
+	/**
114
+	 * Table of new objects not yet inserted in database or objects modified that must be saved.
115
+	 *
116
+	 * @var \SplObjectStorage of DbRow objects
117
+	 */
118
+	private $toSaveObjects;
119
+
120
+	/**
121
+	 * A cache service to be used.
122
+	 *
123
+	 * @var Cache|null
124
+	 */
125
+	private $cache;
126
+
127
+	/**
128
+	 * Map associating a table name to a fully qualified Bean class name.
129
+	 *
130
+	 * @var array
131
+	 */
132
+	private $tableToBeanMap = [];
133
+
134
+	/**
135
+	 * @var \ReflectionClass[]
136
+	 */
137
+	private $reflectionClassCache = array();
138
+
139
+	/**
140
+	 * @var LoggerInterface
141
+	 */
142
+	private $rootLogger;
143
+
144
+	/**
145
+	 * @var LevelFilter|NullLogger
146
+	 */
147
+	private $logger;
148
+
149
+	/**
150
+	 * @var OrderByAnalyzer
151
+	 */
152
+	private $orderByAnalyzer;
153
+
154
+	/**
155
+	 * @var string
156
+	 */
157
+	private $beanNamespace;
158
+
159
+	/**
160
+	 * @var NamingStrategyInterface
161
+	 */
162
+	private $namingStrategy;
163
+	/**
164
+	 * @var ConfigurationInterface
165
+	 */
166
+	private $configuration;
167
+
168
+	/**
169
+	 * @param ConfigurationInterface $configuration The configuration object
170
+	 */
171
+	public function __construct(ConfigurationInterface $configuration)
172
+	{
173
+		if (extension_loaded('weakref')) {
174
+			$this->objectStorage = new WeakrefObjectStorage();
175
+		} else {
176
+			$this->objectStorage = new StandardObjectStorage();
177
+		}
178
+		$this->connection = $configuration->getConnection();
179
+		$this->cache = $configuration->getCache();
180
+		$this->schemaAnalyzer = $configuration->getSchemaAnalyzer();
181
+
182
+		$this->magicQuery = new MagicQuery($this->connection, $this->cache, $this->schemaAnalyzer);
183
+
184
+		$this->tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->connection, $this->cache, $this->schemaAnalyzer);
185
+		$this->cachePrefix = $this->tdbmSchemaAnalyzer->getCachePrefix();
186
+
187
+		$this->toSaveObjects = new \SplObjectStorage();
188
+		$logger = $configuration->getLogger();
189
+		if ($logger === null) {
190
+			$this->logger = new NullLogger();
191
+			$this->rootLogger = new NullLogger();
192
+		} else {
193
+			$this->rootLogger = $logger;
194
+			$this->setLogLevel(LogLevel::WARNING);
195
+		}
196
+		$this->orderByAnalyzer = new OrderByAnalyzer($this->cache, $this->cachePrefix);
197
+		$this->beanNamespace = $configuration->getBeanNamespace();
198
+		$this->namingStrategy = $configuration->getNamingStrategy();
199
+		$this->configuration = $configuration;
200
+	}
201
+
202
+	/**
203
+	 * Returns the object used to connect to the database.
204
+	 *
205
+	 * @return Connection
206
+	 */
207
+	public function getConnection(): Connection
208
+	{
209
+		return $this->connection;
210
+	}
211
+
212
+	/**
213
+	 * Sets the default fetch mode of the result sets returned by `findObjects`.
214
+	 * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY.
215
+	 *
216
+	 * In 'MODE_ARRAY' mode (default), the result is a ResultIterator object that behaves like an array. Use this mode by default (unless the list returned is very big).
217
+	 * In 'MODE_CURSOR' mode, the result is a ResultIterator object. If you scan it many times (by calling several time a foreach loop), the query will be run
218
+	 * several times. In cursor mode, you cannot access the result set by key. Use this mode for large datasets processed by batch.
219
+	 *
220
+	 * @param int $mode
221
+	 *
222
+	 * @return $this
223
+	 *
224
+	 * @throws TDBMException
225
+	 */
226
+	public function setFetchMode($mode)
227
+	{
228
+		if ($mode !== self::MODE_CURSOR && $mode !== self::MODE_ARRAY) {
229
+			throw new TDBMException("Unknown fetch mode: '".$this->mode."'");
230
+		}
231
+		$this->mode = $mode;
232
+
233
+		return $this;
234
+	}
235
+
236
+	/**
237
+	 * Removes the given object from database.
238
+	 * This cannot be called on an object that is not attached to this TDBMService
239
+	 * (will throw a TDBMInvalidOperationException).
240
+	 *
241
+	 * @param AbstractTDBMObject $object the object to delete
242
+	 *
243
+	 * @throws TDBMException
244
+	 * @throws TDBMInvalidOperationException
245
+	 */
246
+	public function delete(AbstractTDBMObject $object)
247
+	{
248
+		switch ($object->_getStatus()) {
249
+			case TDBMObjectStateEnum::STATE_DELETED:
250
+				// Nothing to do, object already deleted.
251
+				return;
252
+			case TDBMObjectStateEnum::STATE_DETACHED:
253
+				throw new TDBMInvalidOperationException('Cannot delete a detached object');
254
+			case TDBMObjectStateEnum::STATE_NEW:
255
+				$this->deleteManyToManyRelationships($object);
256
+				foreach ($object->_getDbRows() as $dbRow) {
257
+					$this->removeFromToSaveObjectList($dbRow);
258
+				}
259
+				break;
260
+			case TDBMObjectStateEnum::STATE_DIRTY:
261
+				foreach ($object->_getDbRows() as $dbRow) {
262
+					$this->removeFromToSaveObjectList($dbRow);
263
+				}
264
+				// And continue deleting...
265
+			case TDBMObjectStateEnum::STATE_NOT_LOADED:
266
+			case TDBMObjectStateEnum::STATE_LOADED:
267
+				$this->deleteManyToManyRelationships($object);
268
+				// Let's delete db rows, in reverse order.
269
+				foreach (array_reverse($object->_getDbRows()) as $dbRow) {
270
+					$tableName = $dbRow->_getDbTableName();
271
+					$primaryKeys = $dbRow->_getPrimaryKeys();
272
+					$this->connection->delete($tableName, $primaryKeys);
273
+					$this->objectStorage->remove($dbRow->_getDbTableName(), $this->getObjectHash($primaryKeys));
274
+				}
275
+				break;
276
+			// @codeCoverageIgnoreStart
277
+			default:
278
+				throw new TDBMInvalidOperationException('Unexpected status for bean');
279
+			// @codeCoverageIgnoreEnd
280
+		}
281
+
282
+		$object->_setStatus(TDBMObjectStateEnum::STATE_DELETED);
283
+	}
284
+
285
+	/**
286
+	 * Removes all many to many relationships for this object.
287
+	 *
288
+	 * @param AbstractTDBMObject $object
289
+	 */
290
+	private function deleteManyToManyRelationships(AbstractTDBMObject $object)
291
+	{
292
+		foreach ($object->_getDbRows() as $tableName => $dbRow) {
293
+			$pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
294
+			foreach ($pivotTables as $pivotTable) {
295
+				$remoteBeans = $object->_getRelationships($pivotTable);
296
+				foreach ($remoteBeans as $remoteBean) {
297
+					$object->_removeRelationship($pivotTable, $remoteBean);
298
+				}
299
+			}
300
+		}
301
+		$this->persistManyToManyRelationships($object);
302
+	}
303
+
304
+	/**
305
+	 * This function removes the given object from the database. It will also remove all objects relied to the one given
306
+	 * by parameter before all.
307
+	 *
308
+	 * Notice: if the object has a multiple primary key, the function will not work.
309
+	 *
310
+	 * @param AbstractTDBMObject $objToDelete
311
+	 */
312
+	public function deleteCascade(AbstractTDBMObject $objToDelete)
313
+	{
314
+		$this->deleteAllConstraintWithThisObject($objToDelete);
315
+		$this->delete($objToDelete);
316
+	}
317
+
318
+	/**
319
+	 * This function is used only in TDBMService (private function)
320
+	 * It will call deleteCascade function foreach object relied with a foreign key to the object given by parameter.
321
+	 *
322
+	 * @param AbstractTDBMObject $obj
323
+	 */
324
+	private function deleteAllConstraintWithThisObject(AbstractTDBMObject $obj)
325
+	{
326
+		$dbRows = $obj->_getDbRows();
327
+		foreach ($dbRows as $dbRow) {
328
+			$tableName = $dbRow->_getDbTableName();
329
+			$pks = array_values($dbRow->_getPrimaryKeys());
330
+			if (!empty($pks)) {
331
+				$incomingFks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($tableName);
332
+
333
+				foreach ($incomingFks as $incomingFk) {
334
+					$filter = array_combine($incomingFk->getLocalColumns(), $pks);
335
+
336
+					$results = $this->findObjects($incomingFk->getLocalTableName(), $filter);
337
+
338
+					foreach ($results as $bean) {
339
+						$this->deleteCascade($bean);
340
+					}
341
+				}
342
+			}
343
+		}
344
+	}
345
+
346
+	/**
347
+	 * This function performs a save() of all the objects that have been modified.
348
+	 */
349
+	public function completeSave()
350
+	{
351
+		foreach ($this->toSaveObjects as $dbRow) {
352
+			$this->save($dbRow->getTDBMObject());
353
+		}
354
+	}
355
+
356
+	/**
357
+	 * Takes in input a filter_bag (which can be about anything from a string to an array of TDBMObjects... see above from documentation),
358
+	 * and gives back a proper Filter object.
359
+	 *
360
+	 * @param mixed $filter_bag
361
+	 * @param int   $counter
362
+	 *
363
+	 * @return array First item: filter string, second item: parameters
364
+	 *
365
+	 * @throws TDBMException
366
+	 */
367
+	public function buildFilterFromFilterBag($filter_bag, $counter = 1)
368
+	{
369
+		if ($filter_bag === null) {
370
+			return ['', []];
371
+		} elseif (is_string($filter_bag)) {
372
+			return [$filter_bag, []];
373
+		} elseif (is_array($filter_bag)) {
374
+			$sqlParts = [];
375
+			$parameters = [];
376
+			foreach ($filter_bag as $column => $value) {
377
+				if (is_int($column)) {
378
+					list($subSqlPart, $subParameters) = $this->buildFilterFromFilterBag($value, $counter);
379
+					$sqlParts[] = $subSqlPart;
380
+					$parameters += $subParameters;
381
+				} else {
382
+					$paramName = 'tdbmparam'.$counter;
383
+					if (is_array($value)) {
384
+						$sqlParts[] = $this->connection->quoteIdentifier($column).' IN :'.$paramName;
385
+					} else {
386
+						$sqlParts[] = $this->connection->quoteIdentifier($column).' = :'.$paramName;
387
+					}
388
+					$parameters[$paramName] = $value;
389
+					++$counter;
390
+				}
391
+			}
392
+
393
+			return [implode(' AND ', $sqlParts), $parameters];
394
+		} elseif ($filter_bag instanceof AbstractTDBMObject) {
395
+			$sqlParts = [];
396
+			$parameters = [];
397
+			$dbRows = $filter_bag->_getDbRows();
398
+			$dbRow = reset($dbRows);
399
+			$primaryKeys = $dbRow->_getPrimaryKeys();
400
+
401
+			foreach ($primaryKeys as $column => $value) {
402
+				$paramName = 'tdbmparam'.$counter;
403
+				$sqlParts[] = $this->connection->quoteIdentifier($dbRow->_getDbTableName()).'.'.$this->connection->quoteIdentifier($column).' = :'.$paramName;
404
+				$parameters[$paramName] = $value;
405
+				++$counter;
406
+			}
407
+
408
+			return [implode(' AND ', $sqlParts), $parameters];
409
+		} elseif ($filter_bag instanceof \Iterator) {
410
+			return $this->buildFilterFromFilterBag(iterator_to_array($filter_bag), $counter);
411
+		} else {
412
+			throw new TDBMException('Error in filter. An object has been passed that is neither a SQL string, nor an array, nor a bean, nor null.');
413
+		}
414
+	}
415
+
416
+	/**
417
+	 * @param string $table
418
+	 *
419
+	 * @return string[]
420
+	 */
421
+	public function getPrimaryKeyColumns($table)
422
+	{
423
+		if (!isset($this->primaryKeysColumns[$table])) {
424
+			$this->primaryKeysColumns[$table] = $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getPrimaryKeyColumns();
425
+
426
+			// TODO TDBM4: See if we need to improve error reporting if table name does not exist.
427
+
428
+			/*$arr = array();
429 429
             foreach ($this->connection->getPrimaryKey($table) as $col) {
430 430
                 $arr[] = $col->name;
431 431
             }
@@ -446,155 +446,155 @@  discard block
 block discarded – undo
446 446
                     throw new TDBMException($str);
447 447
                 }
448 448
             }*/
449
-        }
450
-
451
-        return $this->primaryKeysColumns[$table];
452
-    }
453
-
454
-    /**
455
-     * This is an internal function, you should not use it in your application.
456
-     * This is used internally by TDBM to add an object to the object cache.
457
-     *
458
-     * @param DbRow $dbRow
459
-     */
460
-    public function _addToCache(DbRow $dbRow)
461
-    {
462
-        $primaryKey = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
463
-        $hash = $this->getObjectHash($primaryKey);
464
-        $this->objectStorage->set($dbRow->_getDbTableName(), $hash, $dbRow);
465
-    }
466
-
467
-    /**
468
-     * This is an internal function, you should not use it in your application.
469
-     * This is used internally by TDBM to remove the object from the list of objects that have been
470
-     * created/updated but not saved yet.
471
-     *
472
-     * @param DbRow $myObject
473
-     */
474
-    private function removeFromToSaveObjectList(DbRow $myObject)
475
-    {
476
-        unset($this->toSaveObjects[$myObject]);
477
-    }
478
-
479
-    /**
480
-     * This is an internal function, you should not use it in your application.
481
-     * This is used internally by TDBM to add an object to the list of objects that have been
482
-     * created/updated but not saved yet.
483
-     *
484
-     * @param DbRow $myObject
485
-     */
486
-    public function _addToToSaveObjectList(DbRow $myObject)
487
-    {
488
-        $this->toSaveObjects[$myObject] = true;
489
-    }
490
-
491
-    /**
492
-     * Generates all the daos and beans.
493
-     *
494
-     * @return \string[] the list of tables (key) and bean name (value)
495
-     */
496
-    public function generateAllDaosAndBeans()
497
-    {
498
-        // Purge cache before generating anything.
499
-        $this->cache->deleteAll();
500
-
501
-        $tdbmDaoGenerator = new TDBMDaoGenerator($this->configuration, $this->tdbmSchemaAnalyzer);
502
-        $tdbmDaoGenerator->generateAllDaosAndBeans();
503
-    }
504
-
505
-    /**
506
-     * Returns the fully qualified class name of the bean associated with table $tableName.
507
-     *
508
-     *
509
-     * @param string $tableName
510
-     *
511
-     * @return string
512
-     */
513
-    public function getBeanClassName(string $tableName) : string
514
-    {
515
-        if (isset($this->tableToBeanMap[$tableName])) {
516
-            return $this->tableToBeanMap[$tableName];
517
-        } else {
518
-            $className = $this->beanNamespace.'\\'.$this->namingStrategy->getBeanClassName($tableName);
519
-
520
-            if (!class_exists($className)) {
521
-                throw new TDBMInvalidArgumentException(sprintf('Could not find class "%s". Does table "%s" exist? If yes, consider regenerating the DAOs and beans.', $className, $tableName));
522
-            }
523
-
524
-            $this->tableToBeanMap[$tableName] = $className;
525
-            return $className;
526
-        }
527
-    }
528
-
529
-    /**
530
-     * Saves $object by INSERTing or UPDAT(E)ing it in the database.
531
-     *
532
-     * @param AbstractTDBMObject $object
533
-     *
534
-     * @throws TDBMException
535
-     */
536
-    public function save(AbstractTDBMObject $object)
537
-    {
538
-        $status = $object->_getStatus();
539
-
540
-        if ($status === null) {
541
-            throw new TDBMException(sprintf('Your bean for class %s has no status. It is likely that you overloaded the __construct method and forgot to call parent::__construct.', get_class($object)));
542
-        }
543
-
544
-        // Let's attach this object if it is in detached state.
545
-        if ($status === TDBMObjectStateEnum::STATE_DETACHED) {
546
-            $object->_attach($this);
547
-            $status = $object->_getStatus();
548
-        }
549
-
550
-        if ($status === TDBMObjectStateEnum::STATE_NEW) {
551
-            $dbRows = $object->_getDbRows();
552
-
553
-            $unindexedPrimaryKeys = array();
554
-
555
-            foreach ($dbRows as $dbRow) {
556
-                if ($dbRow->_getStatus() == TDBMObjectStateEnum::STATE_SAVING) {
557
-                    throw TDBMCyclicReferenceException::createCyclicReference($dbRow->_getDbTableName(), $object);
558
-                }
559
-                $dbRow->_setStatus(TDBMObjectStateEnum::STATE_SAVING);
560
-                $tableName = $dbRow->_getDbTableName();
561
-
562
-                $schema = $this->tdbmSchemaAnalyzer->getSchema();
563
-                $tableDescriptor = $schema->getTable($tableName);
564
-
565
-                $primaryKeyColumns = $this->getPrimaryKeyColumns($tableName);
566
-
567
-                $references = $dbRow->_getReferences();
568
-
569
-                // Let's save all references in NEW or DETACHED state (we need their primary key)
570
-                foreach ($references as $fkName => $reference) {
571
-                    if ($reference !== null) {
572
-                        $refStatus = $reference->_getStatus();
573
-                        if ($refStatus === TDBMObjectStateEnum::STATE_NEW || $refStatus === TDBMObjectStateEnum::STATE_DETACHED) {
574
-                            try {
575
-                                $this->save($reference);
576
-                            } catch (TDBMCyclicReferenceException $e) {
577
-                                throw TDBMCyclicReferenceException::extendCyclicReference($e, $dbRow->_getDbTableName(), $object, $fkName);
578
-                            }
579
-                        }
580
-                    }
581
-                }
582
-
583
-                if (empty($unindexedPrimaryKeys)) {
584
-                    $primaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
585
-                } else {
586
-                    // First insert, the children must have the same primary key as the parent.
587
-                    $primaryKeys = $this->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $unindexedPrimaryKeys);
588
-                    $dbRow->_setPrimaryKeys($primaryKeys);
589
-                }
590
-
591
-                $dbRowData = $dbRow->_getDbRow();
592
-
593
-                // Let's see if the columns for primary key have been set before inserting.
594
-                // We assume that if one of the value of the PK has been set, the PK is set.
595
-                $isPkSet = !empty($primaryKeys);
596
-
597
-                /*if (!$isPkSet) {
449
+		}
450
+
451
+		return $this->primaryKeysColumns[$table];
452
+	}
453
+
454
+	/**
455
+	 * This is an internal function, you should not use it in your application.
456
+	 * This is used internally by TDBM to add an object to the object cache.
457
+	 *
458
+	 * @param DbRow $dbRow
459
+	 */
460
+	public function _addToCache(DbRow $dbRow)
461
+	{
462
+		$primaryKey = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
463
+		$hash = $this->getObjectHash($primaryKey);
464
+		$this->objectStorage->set($dbRow->_getDbTableName(), $hash, $dbRow);
465
+	}
466
+
467
+	/**
468
+	 * This is an internal function, you should not use it in your application.
469
+	 * This is used internally by TDBM to remove the object from the list of objects that have been
470
+	 * created/updated but not saved yet.
471
+	 *
472
+	 * @param DbRow $myObject
473
+	 */
474
+	private function removeFromToSaveObjectList(DbRow $myObject)
475
+	{
476
+		unset($this->toSaveObjects[$myObject]);
477
+	}
478
+
479
+	/**
480
+	 * This is an internal function, you should not use it in your application.
481
+	 * This is used internally by TDBM to add an object to the list of objects that have been
482
+	 * created/updated but not saved yet.
483
+	 *
484
+	 * @param DbRow $myObject
485
+	 */
486
+	public function _addToToSaveObjectList(DbRow $myObject)
487
+	{
488
+		$this->toSaveObjects[$myObject] = true;
489
+	}
490
+
491
+	/**
492
+	 * Generates all the daos and beans.
493
+	 *
494
+	 * @return \string[] the list of tables (key) and bean name (value)
495
+	 */
496
+	public function generateAllDaosAndBeans()
497
+	{
498
+		// Purge cache before generating anything.
499
+		$this->cache->deleteAll();
500
+
501
+		$tdbmDaoGenerator = new TDBMDaoGenerator($this->configuration, $this->tdbmSchemaAnalyzer);
502
+		$tdbmDaoGenerator->generateAllDaosAndBeans();
503
+	}
504
+
505
+	/**
506
+	 * Returns the fully qualified class name of the bean associated with table $tableName.
507
+	 *
508
+	 *
509
+	 * @param string $tableName
510
+	 *
511
+	 * @return string
512
+	 */
513
+	public function getBeanClassName(string $tableName) : string
514
+	{
515
+		if (isset($this->tableToBeanMap[$tableName])) {
516
+			return $this->tableToBeanMap[$tableName];
517
+		} else {
518
+			$className = $this->beanNamespace.'\\'.$this->namingStrategy->getBeanClassName($tableName);
519
+
520
+			if (!class_exists($className)) {
521
+				throw new TDBMInvalidArgumentException(sprintf('Could not find class "%s". Does table "%s" exist? If yes, consider regenerating the DAOs and beans.', $className, $tableName));
522
+			}
523
+
524
+			$this->tableToBeanMap[$tableName] = $className;
525
+			return $className;
526
+		}
527
+	}
528
+
529
+	/**
530
+	 * Saves $object by INSERTing or UPDAT(E)ing it in the database.
531
+	 *
532
+	 * @param AbstractTDBMObject $object
533
+	 *
534
+	 * @throws TDBMException
535
+	 */
536
+	public function save(AbstractTDBMObject $object)
537
+	{
538
+		$status = $object->_getStatus();
539
+
540
+		if ($status === null) {
541
+			throw new TDBMException(sprintf('Your bean for class %s has no status. It is likely that you overloaded the __construct method and forgot to call parent::__construct.', get_class($object)));
542
+		}
543
+
544
+		// Let's attach this object if it is in detached state.
545
+		if ($status === TDBMObjectStateEnum::STATE_DETACHED) {
546
+			$object->_attach($this);
547
+			$status = $object->_getStatus();
548
+		}
549
+
550
+		if ($status === TDBMObjectStateEnum::STATE_NEW) {
551
+			$dbRows = $object->_getDbRows();
552
+
553
+			$unindexedPrimaryKeys = array();
554
+
555
+			foreach ($dbRows as $dbRow) {
556
+				if ($dbRow->_getStatus() == TDBMObjectStateEnum::STATE_SAVING) {
557
+					throw TDBMCyclicReferenceException::createCyclicReference($dbRow->_getDbTableName(), $object);
558
+				}
559
+				$dbRow->_setStatus(TDBMObjectStateEnum::STATE_SAVING);
560
+				$tableName = $dbRow->_getDbTableName();
561
+
562
+				$schema = $this->tdbmSchemaAnalyzer->getSchema();
563
+				$tableDescriptor = $schema->getTable($tableName);
564
+
565
+				$primaryKeyColumns = $this->getPrimaryKeyColumns($tableName);
566
+
567
+				$references = $dbRow->_getReferences();
568
+
569
+				// Let's save all references in NEW or DETACHED state (we need their primary key)
570
+				foreach ($references as $fkName => $reference) {
571
+					if ($reference !== null) {
572
+						$refStatus = $reference->_getStatus();
573
+						if ($refStatus === TDBMObjectStateEnum::STATE_NEW || $refStatus === TDBMObjectStateEnum::STATE_DETACHED) {
574
+							try {
575
+								$this->save($reference);
576
+							} catch (TDBMCyclicReferenceException $e) {
577
+								throw TDBMCyclicReferenceException::extendCyclicReference($e, $dbRow->_getDbTableName(), $object, $fkName);
578
+							}
579
+						}
580
+					}
581
+				}
582
+
583
+				if (empty($unindexedPrimaryKeys)) {
584
+					$primaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
585
+				} else {
586
+					// First insert, the children must have the same primary key as the parent.
587
+					$primaryKeys = $this->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $unindexedPrimaryKeys);
588
+					$dbRow->_setPrimaryKeys($primaryKeys);
589
+				}
590
+
591
+				$dbRowData = $dbRow->_getDbRow();
592
+
593
+				// Let's see if the columns for primary key have been set before inserting.
594
+				// We assume that if one of the value of the PK has been set, the PK is set.
595
+				$isPkSet = !empty($primaryKeys);
596
+
597
+				/*if (!$isPkSet) {
598 598
                     // if there is no autoincrement and no pk set, let's go in error.
599 599
                     $isAutoIncrement = true;
600 600
 
@@ -612,30 +612,30 @@  discard block
 block discarded – undo
612 612
 
613 613
                 }*/
614 614
 
615
-                $types = [];
616
-                $escapedDbRowData = [];
615
+				$types = [];
616
+				$escapedDbRowData = [];
617 617
 
618
-                foreach ($dbRowData as $columnName => $value) {
619
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
620
-                    $types[] = $columnDescriptor->getType();
621
-                    $escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
622
-                }
618
+				foreach ($dbRowData as $columnName => $value) {
619
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
620
+					$types[] = $columnDescriptor->getType();
621
+					$escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
622
+				}
623 623
 
624
-                $this->connection->insert($tableName, $escapedDbRowData, $types);
624
+				$this->connection->insert($tableName, $escapedDbRowData, $types);
625 625
 
626
-                if (!$isPkSet && count($primaryKeyColumns) == 1) {
627
-                    $id = $this->connection->lastInsertId();
628
-                    $pkColumn = $primaryKeyColumns[0];
629
-                    // lastInsertId returns a string but the column type is usually a int. Let's convert it back to the correct type.
630
-                    $id = $tableDescriptor->getColumn($pkColumn)->getType()->convertToPHPValue($id, $this->getConnection()->getDatabasePlatform());
631
-                    $primaryKeys[$pkColumn] = $id;
632
-                }
626
+				if (!$isPkSet && count($primaryKeyColumns) == 1) {
627
+					$id = $this->connection->lastInsertId();
628
+					$pkColumn = $primaryKeyColumns[0];
629
+					// lastInsertId returns a string but the column type is usually a int. Let's convert it back to the correct type.
630
+					$id = $tableDescriptor->getColumn($pkColumn)->getType()->convertToPHPValue($id, $this->getConnection()->getDatabasePlatform());
631
+					$primaryKeys[$pkColumn] = $id;
632
+				}
633 633
 
634
-                // TODO: change this to some private magic accessor in future
635
-                $dbRow->_setPrimaryKeys($primaryKeys);
636
-                $unindexedPrimaryKeys = array_values($primaryKeys);
634
+				// TODO: change this to some private magic accessor in future
635
+				$dbRow->_setPrimaryKeys($primaryKeys);
636
+				$unindexedPrimaryKeys = array_values($primaryKeys);
637 637
 
638
-                /*
638
+				/*
639 639
                  * When attached, on "save", we check if the column updated is part of a primary key
640 640
                  * If this is part of a primary key, we call the _update_id method that updates the id in the list of known objects.
641 641
                  * This method should first verify that the id is not already used (and is not auto-incremented)
@@ -645,7 +645,7 @@  discard block
 block discarded – undo
645 645
                  *
646 646
                  */
647 647
 
648
-                /*try {
648
+				/*try {
649 649
                     $this->db_connection->exec($sql);
650 650
                 } catch (TDBMException $e) {
651 651
                     $this->db_onerror = true;
@@ -664,405 +664,405 @@  discard block
 block discarded – undo
664 664
                     }
665 665
                 }*/
666 666
 
667
-                // Let's remove this object from the $new_objects static table.
668
-                $this->removeFromToSaveObjectList($dbRow);
669
-
670
-                // TODO: change this behaviour to something more sensible performance-wise
671
-                // Maybe a setting to trigger this globally?
672
-                //$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
673
-                //$this->db_modified_state = false;
674
-                //$dbRow = array();
675
-
676
-                // Let's add this object to the list of objects in cache.
677
-                $this->_addToCache($dbRow);
678
-            }
679
-
680
-            $object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
681
-        } elseif ($status === TDBMObjectStateEnum::STATE_DIRTY) {
682
-            $dbRows = $object->_getDbRows();
683
-
684
-            foreach ($dbRows as $dbRow) {
685
-                $references = $dbRow->_getReferences();
686
-
687
-                // Let's save all references in NEW state (we need their primary key)
688
-                foreach ($references as $fkName => $reference) {
689
-                    if ($reference !== null && $reference->_getStatus() === TDBMObjectStateEnum::STATE_NEW) {
690
-                        $this->save($reference);
691
-                    }
692
-                }
693
-
694
-                // Let's first get the primary keys
695
-                $tableName = $dbRow->_getDbTableName();
696
-                $dbRowData = $dbRow->_getDbRow();
697
-
698
-                $schema = $this->tdbmSchemaAnalyzer->getSchema();
699
-                $tableDescriptor = $schema->getTable($tableName);
700
-
701
-                $primaryKeys = $dbRow->_getPrimaryKeys();
702
-
703
-                $types = [];
704
-                $escapedDbRowData = [];
705
-                $escapedPrimaryKeys = [];
706
-
707
-                foreach ($dbRowData as $columnName => $value) {
708
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
709
-                    $types[] = $columnDescriptor->getType();
710
-                    $escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
711
-                }
712
-                foreach ($primaryKeys as $columnName => $value) {
713
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
714
-                    $types[] = $columnDescriptor->getType();
715
-                    $escapedPrimaryKeys[$this->connection->quoteIdentifier($columnName)] = $value;
716
-                }
717
-
718
-                $this->connection->update($tableName, $escapedDbRowData, $escapedPrimaryKeys, $types);
719
-
720
-                // Let's check if the primary key has been updated...
721
-                $needsUpdatePk = false;
722
-                foreach ($primaryKeys as $column => $value) {
723
-                    if (!isset($dbRowData[$column]) || $dbRowData[$column] != $value) {
724
-                        $needsUpdatePk = true;
725
-                        break;
726
-                    }
727
-                }
728
-                if ($needsUpdatePk) {
729
-                    $this->objectStorage->remove($tableName, $this->getObjectHash($primaryKeys));
730
-                    $newPrimaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
731
-                    $dbRow->_setPrimaryKeys($newPrimaryKeys);
732
-                    $this->objectStorage->set($tableName, $this->getObjectHash($primaryKeys), $dbRow);
733
-                }
734
-
735
-                // Let's remove this object from the list of objects to save.
736
-                $this->removeFromToSaveObjectList($dbRow);
737
-            }
738
-
739
-            $object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
740
-        } elseif ($status === TDBMObjectStateEnum::STATE_DELETED) {
741
-            throw new TDBMInvalidOperationException('This object has been deleted. It cannot be saved.');
742
-        }
743
-
744
-        // Finally, let's save all the many to many relationships to this bean.
745
-        $this->persistManyToManyRelationships($object);
746
-    }
747
-
748
-    private function persistManyToManyRelationships(AbstractTDBMObject $object)
749
-    {
750
-        foreach ($object->_getCachedRelationships() as $pivotTableName => $storage) {
751
-            $tableDescriptor = $this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName);
752
-            list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $object);
753
-
754
-            $toRemoveFromStorage = [];
755
-
756
-            foreach ($storage as $remoteBean) {
757
-                /* @var $remoteBean AbstractTDBMObject */
758
-                $statusArr = $storage[$remoteBean];
759
-                $status = $statusArr['status'];
760
-                $reverse = $statusArr['reverse'];
761
-                if ($reverse) {
762
-                    continue;
763
-                }
764
-
765
-                if ($status === 'new') {
766
-                    $remoteBeanStatus = $remoteBean->_getStatus();
767
-                    if ($remoteBeanStatus === TDBMObjectStateEnum::STATE_NEW || $remoteBeanStatus === TDBMObjectStateEnum::STATE_DETACHED) {
768
-                        // Let's save remote bean if needed.
769
-                        $this->save($remoteBean);
770
-                    }
771
-
772
-                    $filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
773
-
774
-                    $types = [];
775
-                    $escapedFilters = [];
776
-
777
-                    foreach ($filters as $columnName => $value) {
778
-                        $columnDescriptor = $tableDescriptor->getColumn($columnName);
779
-                        $types[] = $columnDescriptor->getType();
780
-                        $escapedFilters[$this->connection->quoteIdentifier($columnName)] = $value;
781
-                    }
782
-
783
-                    $this->connection->insert($pivotTableName, $escapedFilters, $types);
784
-
785
-                    // Finally, let's mark relationships as saved.
786
-                    $statusArr['status'] = 'loaded';
787
-                    $storage[$remoteBean] = $statusArr;
788
-                    $remoteStorage = $remoteBean->_getCachedRelationships()[$pivotTableName];
789
-                    $remoteStatusArr = $remoteStorage[$object];
790
-                    $remoteStatusArr['status'] = 'loaded';
791
-                    $remoteStorage[$object] = $remoteStatusArr;
792
-                } elseif ($status === 'delete') {
793
-                    $filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
794
-
795
-                    $types = [];
796
-
797
-                    foreach ($filters as $columnName => $value) {
798
-                        $columnDescriptor = $tableDescriptor->getColumn($columnName);
799
-                        $types[] = $columnDescriptor->getType();
800
-                    }
801
-
802
-                    $this->connection->delete($pivotTableName, $filters, $types);
803
-
804
-                    // Finally, let's remove relationships completely from bean.
805
-                    $toRemoveFromStorage[] = $remoteBean;
806
-
807
-                    $remoteBean->_getCachedRelationships()[$pivotTableName]->detach($object);
808
-                }
809
-            }
810
-
811
-            // Note: due to https://bugs.php.net/bug.php?id=65629, we cannot delete an element inside a foreach loop on a SplStorageObject.
812
-            // Therefore, we cache elements in the $toRemoveFromStorage to remove them at a later stage.
813
-            foreach ($toRemoveFromStorage as $remoteBean) {
814
-                $storage->detach($remoteBean);
815
-            }
816
-        }
817
-    }
818
-
819
-    private function getPivotFilters(AbstractTDBMObject $localBean, AbstractTDBMObject $remoteBean, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk)
820
-    {
821
-        $localBeanPk = $this->getPrimaryKeyValues($localBean);
822
-        $remoteBeanPk = $this->getPrimaryKeyValues($remoteBean);
823
-        $localColumns = $localFk->getLocalColumns();
824
-        $remoteColumns = $remoteFk->getLocalColumns();
825
-
826
-        $localFilters = array_combine($localColumns, $localBeanPk);
827
-        $remoteFilters = array_combine($remoteColumns, $remoteBeanPk);
828
-
829
-        return array_merge($localFilters, $remoteFilters);
830
-    }
831
-
832
-    /**
833
-     * Returns the "values" of the primary key.
834
-     * This returns the primary key from the $primaryKey attribute, not the one stored in the columns.
835
-     *
836
-     * @param AbstractTDBMObject $bean
837
-     *
838
-     * @return array numerically indexed array of values
839
-     */
840
-    private function getPrimaryKeyValues(AbstractTDBMObject $bean)
841
-    {
842
-        $dbRows = $bean->_getDbRows();
843
-        $dbRow = reset($dbRows);
844
-
845
-        return array_values($dbRow->_getPrimaryKeys());
846
-    }
847
-
848
-    /**
849
-     * Returns a unique hash used to store the object based on its primary key.
850
-     * If the array contains only one value, then the value is returned.
851
-     * Otherwise, a hash representing the array is returned.
852
-     *
853
-     * @param array $primaryKeys An array of columns => values forming the primary key
854
-     *
855
-     * @return string
856
-     */
857
-    public function getObjectHash(array $primaryKeys)
858
-    {
859
-        if (count($primaryKeys) === 1) {
860
-            return reset($primaryKeys);
861
-        } else {
862
-            ksort($primaryKeys);
863
-
864
-            return md5(json_encode($primaryKeys));
865
-        }
866
-    }
867
-
868
-    /**
869
-     * Returns an array of primary keys from the object.
870
-     * The primary keys are extracted from the object columns and not from the primary keys stored in the
871
-     * $primaryKeys variable of the object.
872
-     *
873
-     * @param DbRow $dbRow
874
-     *
875
-     * @return array Returns an array of column => value
876
-     */
877
-    public function getPrimaryKeysForObjectFromDbRow(DbRow $dbRow)
878
-    {
879
-        $table = $dbRow->_getDbTableName();
880
-        $dbRowData = $dbRow->_getDbRow();
881
-
882
-        return $this->_getPrimaryKeysFromObjectData($table, $dbRowData);
883
-    }
884
-
885
-    /**
886
-     * Returns an array of primary keys for the given row.
887
-     * The primary keys are extracted from the object columns.
888
-     *
889
-     * @param $table
890
-     * @param array $columns
891
-     *
892
-     * @return array
893
-     */
894
-    public function _getPrimaryKeysFromObjectData($table, array $columns)
895
-    {
896
-        $primaryKeyColumns = $this->getPrimaryKeyColumns($table);
897
-        $values = array();
898
-        foreach ($primaryKeyColumns as $column) {
899
-            if (isset($columns[$column])) {
900
-                $values[$column] = $columns[$column];
901
-            }
902
-        }
903
-
904
-        return $values;
905
-    }
906
-
907
-    /**
908
-     * Attaches $object to this TDBMService.
909
-     * The $object must be in DETACHED state and will pass in NEW state.
910
-     *
911
-     * @param AbstractTDBMObject $object
912
-     *
913
-     * @throws TDBMInvalidOperationException
914
-     */
915
-    public function attach(AbstractTDBMObject $object)
916
-    {
917
-        $object->_attach($this);
918
-    }
919
-
920
-    /**
921
-     * Returns an associative array (column => value) for the primary keys from the table name and an
922
-     * indexed array of primary key values.
923
-     *
924
-     * @param string $tableName
925
-     * @param array  $indexedPrimaryKeys
926
-     */
927
-    public function _getPrimaryKeysFromIndexedPrimaryKeys($tableName, array $indexedPrimaryKeys)
928
-    {
929
-        $primaryKeyColumns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getPrimaryKeyColumns();
930
-
931
-        if (count($primaryKeyColumns) !== count($indexedPrimaryKeys)) {
932
-            throw new TDBMException(sprintf('Wrong number of columns passed for primary key. Expected %s columns for table "%s",
667
+				// Let's remove this object from the $new_objects static table.
668
+				$this->removeFromToSaveObjectList($dbRow);
669
+
670
+				// TODO: change this behaviour to something more sensible performance-wise
671
+				// Maybe a setting to trigger this globally?
672
+				//$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
673
+				//$this->db_modified_state = false;
674
+				//$dbRow = array();
675
+
676
+				// Let's add this object to the list of objects in cache.
677
+				$this->_addToCache($dbRow);
678
+			}
679
+
680
+			$object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
681
+		} elseif ($status === TDBMObjectStateEnum::STATE_DIRTY) {
682
+			$dbRows = $object->_getDbRows();
683
+
684
+			foreach ($dbRows as $dbRow) {
685
+				$references = $dbRow->_getReferences();
686
+
687
+				// Let's save all references in NEW state (we need their primary key)
688
+				foreach ($references as $fkName => $reference) {
689
+					if ($reference !== null && $reference->_getStatus() === TDBMObjectStateEnum::STATE_NEW) {
690
+						$this->save($reference);
691
+					}
692
+				}
693
+
694
+				// Let's first get the primary keys
695
+				$tableName = $dbRow->_getDbTableName();
696
+				$dbRowData = $dbRow->_getDbRow();
697
+
698
+				$schema = $this->tdbmSchemaAnalyzer->getSchema();
699
+				$tableDescriptor = $schema->getTable($tableName);
700
+
701
+				$primaryKeys = $dbRow->_getPrimaryKeys();
702
+
703
+				$types = [];
704
+				$escapedDbRowData = [];
705
+				$escapedPrimaryKeys = [];
706
+
707
+				foreach ($dbRowData as $columnName => $value) {
708
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
709
+					$types[] = $columnDescriptor->getType();
710
+					$escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
711
+				}
712
+				foreach ($primaryKeys as $columnName => $value) {
713
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
714
+					$types[] = $columnDescriptor->getType();
715
+					$escapedPrimaryKeys[$this->connection->quoteIdentifier($columnName)] = $value;
716
+				}
717
+
718
+				$this->connection->update($tableName, $escapedDbRowData, $escapedPrimaryKeys, $types);
719
+
720
+				// Let's check if the primary key has been updated...
721
+				$needsUpdatePk = false;
722
+				foreach ($primaryKeys as $column => $value) {
723
+					if (!isset($dbRowData[$column]) || $dbRowData[$column] != $value) {
724
+						$needsUpdatePk = true;
725
+						break;
726
+					}
727
+				}
728
+				if ($needsUpdatePk) {
729
+					$this->objectStorage->remove($tableName, $this->getObjectHash($primaryKeys));
730
+					$newPrimaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
731
+					$dbRow->_setPrimaryKeys($newPrimaryKeys);
732
+					$this->objectStorage->set($tableName, $this->getObjectHash($primaryKeys), $dbRow);
733
+				}
734
+
735
+				// Let's remove this object from the list of objects to save.
736
+				$this->removeFromToSaveObjectList($dbRow);
737
+			}
738
+
739
+			$object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
740
+		} elseif ($status === TDBMObjectStateEnum::STATE_DELETED) {
741
+			throw new TDBMInvalidOperationException('This object has been deleted. It cannot be saved.');
742
+		}
743
+
744
+		// Finally, let's save all the many to many relationships to this bean.
745
+		$this->persistManyToManyRelationships($object);
746
+	}
747
+
748
+	private function persistManyToManyRelationships(AbstractTDBMObject $object)
749
+	{
750
+		foreach ($object->_getCachedRelationships() as $pivotTableName => $storage) {
751
+			$tableDescriptor = $this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName);
752
+			list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $object);
753
+
754
+			$toRemoveFromStorage = [];
755
+
756
+			foreach ($storage as $remoteBean) {
757
+				/* @var $remoteBean AbstractTDBMObject */
758
+				$statusArr = $storage[$remoteBean];
759
+				$status = $statusArr['status'];
760
+				$reverse = $statusArr['reverse'];
761
+				if ($reverse) {
762
+					continue;
763
+				}
764
+
765
+				if ($status === 'new') {
766
+					$remoteBeanStatus = $remoteBean->_getStatus();
767
+					if ($remoteBeanStatus === TDBMObjectStateEnum::STATE_NEW || $remoteBeanStatus === TDBMObjectStateEnum::STATE_DETACHED) {
768
+						// Let's save remote bean if needed.
769
+						$this->save($remoteBean);
770
+					}
771
+
772
+					$filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
773
+
774
+					$types = [];
775
+					$escapedFilters = [];
776
+
777
+					foreach ($filters as $columnName => $value) {
778
+						$columnDescriptor = $tableDescriptor->getColumn($columnName);
779
+						$types[] = $columnDescriptor->getType();
780
+						$escapedFilters[$this->connection->quoteIdentifier($columnName)] = $value;
781
+					}
782
+
783
+					$this->connection->insert($pivotTableName, $escapedFilters, $types);
784
+
785
+					// Finally, let's mark relationships as saved.
786
+					$statusArr['status'] = 'loaded';
787
+					$storage[$remoteBean] = $statusArr;
788
+					$remoteStorage = $remoteBean->_getCachedRelationships()[$pivotTableName];
789
+					$remoteStatusArr = $remoteStorage[$object];
790
+					$remoteStatusArr['status'] = 'loaded';
791
+					$remoteStorage[$object] = $remoteStatusArr;
792
+				} elseif ($status === 'delete') {
793
+					$filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
794
+
795
+					$types = [];
796
+
797
+					foreach ($filters as $columnName => $value) {
798
+						$columnDescriptor = $tableDescriptor->getColumn($columnName);
799
+						$types[] = $columnDescriptor->getType();
800
+					}
801
+
802
+					$this->connection->delete($pivotTableName, $filters, $types);
803
+
804
+					// Finally, let's remove relationships completely from bean.
805
+					$toRemoveFromStorage[] = $remoteBean;
806
+
807
+					$remoteBean->_getCachedRelationships()[$pivotTableName]->detach($object);
808
+				}
809
+			}
810
+
811
+			// Note: due to https://bugs.php.net/bug.php?id=65629, we cannot delete an element inside a foreach loop on a SplStorageObject.
812
+			// Therefore, we cache elements in the $toRemoveFromStorage to remove them at a later stage.
813
+			foreach ($toRemoveFromStorage as $remoteBean) {
814
+				$storage->detach($remoteBean);
815
+			}
816
+		}
817
+	}
818
+
819
+	private function getPivotFilters(AbstractTDBMObject $localBean, AbstractTDBMObject $remoteBean, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk)
820
+	{
821
+		$localBeanPk = $this->getPrimaryKeyValues($localBean);
822
+		$remoteBeanPk = $this->getPrimaryKeyValues($remoteBean);
823
+		$localColumns = $localFk->getLocalColumns();
824
+		$remoteColumns = $remoteFk->getLocalColumns();
825
+
826
+		$localFilters = array_combine($localColumns, $localBeanPk);
827
+		$remoteFilters = array_combine($remoteColumns, $remoteBeanPk);
828
+
829
+		return array_merge($localFilters, $remoteFilters);
830
+	}
831
+
832
+	/**
833
+	 * Returns the "values" of the primary key.
834
+	 * This returns the primary key from the $primaryKey attribute, not the one stored in the columns.
835
+	 *
836
+	 * @param AbstractTDBMObject $bean
837
+	 *
838
+	 * @return array numerically indexed array of values
839
+	 */
840
+	private function getPrimaryKeyValues(AbstractTDBMObject $bean)
841
+	{
842
+		$dbRows = $bean->_getDbRows();
843
+		$dbRow = reset($dbRows);
844
+
845
+		return array_values($dbRow->_getPrimaryKeys());
846
+	}
847
+
848
+	/**
849
+	 * Returns a unique hash used to store the object based on its primary key.
850
+	 * If the array contains only one value, then the value is returned.
851
+	 * Otherwise, a hash representing the array is returned.
852
+	 *
853
+	 * @param array $primaryKeys An array of columns => values forming the primary key
854
+	 *
855
+	 * @return string
856
+	 */
857
+	public function getObjectHash(array $primaryKeys)
858
+	{
859
+		if (count($primaryKeys) === 1) {
860
+			return reset($primaryKeys);
861
+		} else {
862
+			ksort($primaryKeys);
863
+
864
+			return md5(json_encode($primaryKeys));
865
+		}
866
+	}
867
+
868
+	/**
869
+	 * Returns an array of primary keys from the object.
870
+	 * The primary keys are extracted from the object columns and not from the primary keys stored in the
871
+	 * $primaryKeys variable of the object.
872
+	 *
873
+	 * @param DbRow $dbRow
874
+	 *
875
+	 * @return array Returns an array of column => value
876
+	 */
877
+	public function getPrimaryKeysForObjectFromDbRow(DbRow $dbRow)
878
+	{
879
+		$table = $dbRow->_getDbTableName();
880
+		$dbRowData = $dbRow->_getDbRow();
881
+
882
+		return $this->_getPrimaryKeysFromObjectData($table, $dbRowData);
883
+	}
884
+
885
+	/**
886
+	 * Returns an array of primary keys for the given row.
887
+	 * The primary keys are extracted from the object columns.
888
+	 *
889
+	 * @param $table
890
+	 * @param array $columns
891
+	 *
892
+	 * @return array
893
+	 */
894
+	public function _getPrimaryKeysFromObjectData($table, array $columns)
895
+	{
896
+		$primaryKeyColumns = $this->getPrimaryKeyColumns($table);
897
+		$values = array();
898
+		foreach ($primaryKeyColumns as $column) {
899
+			if (isset($columns[$column])) {
900
+				$values[$column] = $columns[$column];
901
+			}
902
+		}
903
+
904
+		return $values;
905
+	}
906
+
907
+	/**
908
+	 * Attaches $object to this TDBMService.
909
+	 * The $object must be in DETACHED state and will pass in NEW state.
910
+	 *
911
+	 * @param AbstractTDBMObject $object
912
+	 *
913
+	 * @throws TDBMInvalidOperationException
914
+	 */
915
+	public function attach(AbstractTDBMObject $object)
916
+	{
917
+		$object->_attach($this);
918
+	}
919
+
920
+	/**
921
+	 * Returns an associative array (column => value) for the primary keys from the table name and an
922
+	 * indexed array of primary key values.
923
+	 *
924
+	 * @param string $tableName
925
+	 * @param array  $indexedPrimaryKeys
926
+	 */
927
+	public function _getPrimaryKeysFromIndexedPrimaryKeys($tableName, array $indexedPrimaryKeys)
928
+	{
929
+		$primaryKeyColumns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getPrimaryKeyColumns();
930
+
931
+		if (count($primaryKeyColumns) !== count($indexedPrimaryKeys)) {
932
+			throw new TDBMException(sprintf('Wrong number of columns passed for primary key. Expected %s columns for table "%s",
933 933
 			got %s instead.', count($primaryKeyColumns), $tableName, count($indexedPrimaryKeys)));
934
-        }
935
-
936
-        return array_combine($primaryKeyColumns, $indexedPrimaryKeys);
937
-    }
938
-
939
-    /**
940
-     * Return the list of tables (from child to parent) joining the tables passed in parameter.
941
-     * Tables must be in a single line of inheritance. The method will find missing tables.
942
-     *
943
-     * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
944
-     * we must be able to find all other tables.
945
-     *
946
-     * @param string[] $tables
947
-     *
948
-     * @return string[]
949
-     */
950
-    public function _getLinkBetweenInheritedTables(array $tables)
951
-    {
952
-        sort($tables);
953
-
954
-        return $this->fromCache($this->cachePrefix.'_linkbetweeninheritedtables_'.implode('__split__', $tables),
955
-            function () use ($tables) {
956
-                return $this->_getLinkBetweenInheritedTablesWithoutCache($tables);
957
-            });
958
-    }
959
-
960
-    /**
961
-     * Return the list of tables (from child to parent) joining the tables passed in parameter.
962
-     * Tables must be in a single line of inheritance. The method will find missing tables.
963
-     *
964
-     * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
965
-     * we must be able to find all other tables.
966
-     *
967
-     * @param string[] $tables
968
-     *
969
-     * @return string[]
970
-     */
971
-    private function _getLinkBetweenInheritedTablesWithoutCache(array $tables)
972
-    {
973
-        $schemaAnalyzer = $this->schemaAnalyzer;
974
-
975
-        foreach ($tables as $currentTable) {
976
-            $allParents = [$currentTable];
977
-            while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
978
-                $currentTable = $currentFk->getForeignTableName();
979
-                $allParents[] = $currentTable;
980
-            }
981
-
982
-            // Now, does the $allParents contain all the tables we want?
983
-            $notFoundTables = array_diff($tables, $allParents);
984
-            if (empty($notFoundTables)) {
985
-                // We have a winner!
986
-                return $allParents;
987
-            }
988
-        }
989
-
990
-        throw TDBMInheritanceException::create($tables);
991
-    }
992
-
993
-    /**
994
-     * Returns the list of tables related to this table (via a parent or child inheritance relationship).
995
-     *
996
-     * @param string $table
997
-     *
998
-     * @return string[]
999
-     */
1000
-    public function _getRelatedTablesByInheritance($table)
1001
-    {
1002
-        return $this->fromCache($this->cachePrefix.'_relatedtables_'.$table, function () use ($table) {
1003
-            return $this->_getRelatedTablesByInheritanceWithoutCache($table);
1004
-        });
1005
-    }
1006
-
1007
-    /**
1008
-     * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1009
-     *
1010
-     * @param string $table
1011
-     *
1012
-     * @return string[]
1013
-     */
1014
-    private function _getRelatedTablesByInheritanceWithoutCache($table)
1015
-    {
1016
-        $schemaAnalyzer = $this->schemaAnalyzer;
1017
-
1018
-        // Let's scan the parent tables
1019
-        $currentTable = $table;
1020
-
1021
-        $parentTables = [];
1022
-
1023
-        // Get parent relationship
1024
-        while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
1025
-            $currentTable = $currentFk->getForeignTableName();
1026
-            $parentTables[] = $currentTable;
1027
-        }
1028
-
1029
-        // Let's recurse in children
1030
-        $childrenTables = $this->exploreChildrenTablesRelationships($schemaAnalyzer, $table);
1031
-
1032
-        return array_merge(array_reverse($parentTables), $childrenTables);
1033
-    }
1034
-
1035
-    /**
1036
-     * Explore all the children and descendant of $table and returns ForeignKeyConstraints on those.
1037
-     *
1038
-     * @param string $table
1039
-     *
1040
-     * @return string[]
1041
-     */
1042
-    private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyzer, $table)
1043
-    {
1044
-        $tables = [$table];
1045
-        $keys = $schemaAnalyzer->getChildrenRelationships($table);
1046
-
1047
-        foreach ($keys as $key) {
1048
-            $tables = array_merge($tables, $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName()));
1049
-        }
1050
-
1051
-        return $tables;
1052
-    }
1053
-
1054
-    /**
1055
-     * Casts a foreign key into SQL, assuming table name is used with no alias.
1056
-     * The returned value does contain only one table. For instance:.
1057
-     *
1058
-     * " LEFT JOIN table2 ON table1.id = table2.table1_id"
1059
-     *
1060
-     * @param ForeignKeyConstraint $fk
1061
-     * @param bool                 $leftTableIsLocal
1062
-     *
1063
-     * @return string
1064
-     */
1065
-    /*private function foreignKeyToSql(ForeignKeyConstraint $fk, $leftTableIsLocal) {
934
+		}
935
+
936
+		return array_combine($primaryKeyColumns, $indexedPrimaryKeys);
937
+	}
938
+
939
+	/**
940
+	 * Return the list of tables (from child to parent) joining the tables passed in parameter.
941
+	 * Tables must be in a single line of inheritance. The method will find missing tables.
942
+	 *
943
+	 * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
944
+	 * we must be able to find all other tables.
945
+	 *
946
+	 * @param string[] $tables
947
+	 *
948
+	 * @return string[]
949
+	 */
950
+	public function _getLinkBetweenInheritedTables(array $tables)
951
+	{
952
+		sort($tables);
953
+
954
+		return $this->fromCache($this->cachePrefix.'_linkbetweeninheritedtables_'.implode('__split__', $tables),
955
+			function () use ($tables) {
956
+				return $this->_getLinkBetweenInheritedTablesWithoutCache($tables);
957
+			});
958
+	}
959
+
960
+	/**
961
+	 * Return the list of tables (from child to parent) joining the tables passed in parameter.
962
+	 * Tables must be in a single line of inheritance. The method will find missing tables.
963
+	 *
964
+	 * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
965
+	 * we must be able to find all other tables.
966
+	 *
967
+	 * @param string[] $tables
968
+	 *
969
+	 * @return string[]
970
+	 */
971
+	private function _getLinkBetweenInheritedTablesWithoutCache(array $tables)
972
+	{
973
+		$schemaAnalyzer = $this->schemaAnalyzer;
974
+
975
+		foreach ($tables as $currentTable) {
976
+			$allParents = [$currentTable];
977
+			while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
978
+				$currentTable = $currentFk->getForeignTableName();
979
+				$allParents[] = $currentTable;
980
+			}
981
+
982
+			// Now, does the $allParents contain all the tables we want?
983
+			$notFoundTables = array_diff($tables, $allParents);
984
+			if (empty($notFoundTables)) {
985
+				// We have a winner!
986
+				return $allParents;
987
+			}
988
+		}
989
+
990
+		throw TDBMInheritanceException::create($tables);
991
+	}
992
+
993
+	/**
994
+	 * Returns the list of tables related to this table (via a parent or child inheritance relationship).
995
+	 *
996
+	 * @param string $table
997
+	 *
998
+	 * @return string[]
999
+	 */
1000
+	public function _getRelatedTablesByInheritance($table)
1001
+	{
1002
+		return $this->fromCache($this->cachePrefix.'_relatedtables_'.$table, function () use ($table) {
1003
+			return $this->_getRelatedTablesByInheritanceWithoutCache($table);
1004
+		});
1005
+	}
1006
+
1007
+	/**
1008
+	 * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1009
+	 *
1010
+	 * @param string $table
1011
+	 *
1012
+	 * @return string[]
1013
+	 */
1014
+	private function _getRelatedTablesByInheritanceWithoutCache($table)
1015
+	{
1016
+		$schemaAnalyzer = $this->schemaAnalyzer;
1017
+
1018
+		// Let's scan the parent tables
1019
+		$currentTable = $table;
1020
+
1021
+		$parentTables = [];
1022
+
1023
+		// Get parent relationship
1024
+		while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
1025
+			$currentTable = $currentFk->getForeignTableName();
1026
+			$parentTables[] = $currentTable;
1027
+		}
1028
+
1029
+		// Let's recurse in children
1030
+		$childrenTables = $this->exploreChildrenTablesRelationships($schemaAnalyzer, $table);
1031
+
1032
+		return array_merge(array_reverse($parentTables), $childrenTables);
1033
+	}
1034
+
1035
+	/**
1036
+	 * Explore all the children and descendant of $table and returns ForeignKeyConstraints on those.
1037
+	 *
1038
+	 * @param string $table
1039
+	 *
1040
+	 * @return string[]
1041
+	 */
1042
+	private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyzer, $table)
1043
+	{
1044
+		$tables = [$table];
1045
+		$keys = $schemaAnalyzer->getChildrenRelationships($table);
1046
+
1047
+		foreach ($keys as $key) {
1048
+			$tables = array_merge($tables, $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName()));
1049
+		}
1050
+
1051
+		return $tables;
1052
+	}
1053
+
1054
+	/**
1055
+	 * Casts a foreign key into SQL, assuming table name is used with no alias.
1056
+	 * The returned value does contain only one table. For instance:.
1057
+	 *
1058
+	 * " LEFT JOIN table2 ON table1.id = table2.table1_id"
1059
+	 *
1060
+	 * @param ForeignKeyConstraint $fk
1061
+	 * @param bool                 $leftTableIsLocal
1062
+	 *
1063
+	 * @return string
1064
+	 */
1065
+	/*private function foreignKeyToSql(ForeignKeyConstraint $fk, $leftTableIsLocal) {
1066 1066
         $onClauses = [];
1067 1067
         $foreignTableName = $this->connection->quoteIdentifier($fk->getForeignTableName());
1068 1068
         $foreignColumns = $fk->getForeignColumns();
@@ -1088,417 +1088,417 @@  discard block
 block discarded – undo
1088 1088
         }
1089 1089
     }*/
1090 1090
 
1091
-    /**
1092
-     * Returns a `ResultIterator` object representing filtered records of "$mainTable" .
1093
-     *
1094
-     * The findObjects method should be the most used query method in TDBM if you want to query the database for objects.
1095
-     * (Note: if you want to query the database for an object by its primary key, use the findObjectByPk method).
1096
-     *
1097
-     * The findObjects method takes in parameter:
1098
-     * 	- mainTable: the kind of bean you want to retrieve. In TDBM, a bean matches a database row, so the
1099
-     * 			`$mainTable` parameter should be the name of an existing table in database.
1100
-     *  - filter: The filter is a filter bag. It is what you use to filter your request (the WHERE part in SQL).
1101
-     *          It can be a string (SQL Where clause), or even a bean or an associative array (key = column to filter, value = value to find)
1102
-     *  - parameters: The parameters used in the filter. If you pass a SQL string as a filter, be sure to avoid
1103
-     *          concatenating parameters in the string (this leads to SQL injection and also to poor caching performance).
1104
-     *          Instead, please consider passing parameters (see documentation for more details).
1105
-     *  - additionalTablesFetch: An array of SQL tables names. The beans related to those tables will be fetched along
1106
-     *          the main table. This is useful to avoid hitting the database with numerous subqueries.
1107
-     *  - mode: The fetch mode of the result. See `setFetchMode()` method for more details.
1108
-     *
1109
-     * The `findObjects` method will return a `ResultIterator`. A `ResultIterator` is an object that behaves as an array
1110
-     * (in ARRAY mode) at least. It can be iterated using a `foreach` loop.
1111
-     *
1112
-     * Finally, if filter_bag is null, the whole table is returned.
1113
-     *
1114
-     * @param string                       $mainTable             The name of the table queried
1115
-     * @param string|array|null            $filter                The SQL filters to apply to the query (the WHERE part). Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1116
-     * @param array                        $parameters
1117
-     * @param string|UncheckedOrderBy|null $orderString           The ORDER BY part of the query. Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1118
-     * @param array                        $additionalTablesFetch
1119
-     * @param int                          $mode
1120
-     * @param string                       $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1121
-     *
1122
-     * @return ResultIterator An object representing an array of results
1123
-     *
1124
-     * @throws TDBMException
1125
-     */
1126
-    public function findObjects(string $mainTable, $filter = null, array $parameters = array(), $orderString = null, array $additionalTablesFetch = array(), $mode = null, string $className = null)
1127
-    {
1128
-        // $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1129
-        if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1130
-            throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1131
-        }
1132
-
1133
-        $mode = $mode ?: $this->mode;
1134
-
1135
-        list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1136
-
1137
-        $parameters = array_merge($parameters, $additionalParameters);
1138
-
1139
-        $queryFactory = new FindObjectsQueryFactory($mainTable, $additionalTablesFetch, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer);
1140
-
1141
-        return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1142
-    }
1143
-
1144
-    /**
1145
-     * @param string                       $mainTable   The name of the table queried
1146
-     * @param string                       $from        The from sql statement
1147
-     * @param string|array|null            $filter      The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1148
-     * @param array                        $parameters
1149
-     * @param string|UncheckedOrderBy|null $orderString The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column)
1150
-     * @param int                          $mode
1151
-     * @param string                       $className   Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1152
-     *
1153
-     * @return ResultIterator An object representing an array of results
1154
-     *
1155
-     * @throws TDBMException
1156
-     */
1157
-    public function findObjectsFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), $orderString = null, $mode = null, string $className = null)
1158
-    {
1159
-        // $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1160
-        if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1161
-            throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1162
-        }
1163
-
1164
-        $mode = $mode ?: $this->mode;
1165
-
1166
-        list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1167
-
1168
-        $parameters = array_merge($parameters, $additionalParameters);
1169
-
1170
-        $queryFactory = new FindObjectsFromSqlQueryFactory($mainTable, $from, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer, $this->schemaAnalyzer, $this->cache, $this->cachePrefix);
1171
-
1172
-        return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1173
-    }
1174
-
1175
-    /**
1176
-     * @param $table
1177
-     * @param array  $primaryKeys
1178
-     * @param array  $additionalTablesFetch
1179
-     * @param bool   $lazy                  Whether to perform lazy loading on this object or not
1180
-     * @param string $className
1181
-     *
1182
-     * @return AbstractTDBMObject
1183
-     *
1184
-     * @throws TDBMException
1185
-     */
1186
-    public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch = array(), bool $lazy = false, string $className = null)
1187
-    {
1188
-        $primaryKeys = $this->_getPrimaryKeysFromObjectData($table, $primaryKeys);
1189
-        $hash = $this->getObjectHash($primaryKeys);
1190
-
1191
-        if ($this->objectStorage->has($table, $hash)) {
1192
-            $dbRow = $this->objectStorage->get($table, $hash);
1193
-            $bean = $dbRow->getTDBMObject();
1194
-            if ($className !== null && !is_a($bean, $className)) {
1195
-                throw new TDBMException("TDBM cannot create a bean of class '".$className."'. The requested object was already loaded and its class is '".get_class($bean)."'");
1196
-            }
1197
-
1198
-            return $bean;
1199
-        }
1200
-
1201
-        // Are we performing lazy fetching?
1202
-        if ($lazy === true) {
1203
-            // Can we perform lazy fetching?
1204
-            $tables = $this->_getRelatedTablesByInheritance($table);
1205
-            // Only allowed if no inheritance.
1206
-            if (count($tables) === 1) {
1207
-                if ($className === null) {
1208
-                    try {
1209
-                        $className = $this->getBeanClassName($table);
1210
-                    } catch (TDBMInvalidArgumentException $e) {
1211
-                        $className = TDBMObject::class;
1212
-                    }
1213
-                }
1214
-
1215
-                // Let's construct the bean
1216
-                if (!isset($this->reflectionClassCache[$className])) {
1217
-                    $this->reflectionClassCache[$className] = new \ReflectionClass($className);
1218
-                }
1219
-                // Let's bypass the constructor when creating the bean!
1220
-                $bean = $this->reflectionClassCache[$className]->newInstanceWithoutConstructor();
1221
-                /* @var $bean AbstractTDBMObject */
1222
-                $bean->_constructLazy($table, $primaryKeys, $this);
1223
-
1224
-                return $bean;
1225
-            }
1226
-        }
1227
-
1228
-        // Did not find the object in cache? Let's query it!
1229
-        return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className);
1230
-    }
1231
-
1232
-    /**
1233
-     * Returns a unique bean (or null) according to the filters passed in parameter.
1234
-     *
1235
-     * @param string            $mainTable             The name of the table queried
1236
-     * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1237
-     * @param array             $parameters
1238
-     * @param array             $additionalTablesFetch
1239
-     * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1240
-     *
1241
-     * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1242
-     *
1243
-     * @throws TDBMException
1244
-     */
1245
-    public function findObject(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1246
-    {
1247
-        $objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className);
1248
-        $page = $objects->take(0, 2);
1249
-        $count = $page->count();
1250
-        if ($count > 1) {
1251
-            throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1252
-        } elseif ($count === 0) {
1253
-            return;
1254
-        }
1255
-
1256
-        return $page[0];
1257
-    }
1258
-
1259
-    /**
1260
-     * Returns a unique bean (or null) according to the filters passed in parameter.
1261
-     *
1262
-     * @param string            $mainTable  The name of the table queried
1263
-     * @param string            $from       The from sql statement
1264
-     * @param string|array|null $filter     The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1265
-     * @param array             $parameters
1266
-     * @param string            $className  Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1267
-     *
1268
-     * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1269
-     *
1270
-     * @throws TDBMException
1271
-     */
1272
-    public function findObjectFromSql($mainTable, $from, $filter = null, array $parameters = array(), $className = null)
1273
-    {
1274
-        $objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className);
1275
-        $page = $objects->take(0, 2);
1276
-        $count = $page->count();
1277
-        if ($count > 1) {
1278
-            throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1279
-        } elseif ($count === 0) {
1280
-            return;
1281
-        }
1282
-
1283
-        return $page[0];
1284
-    }
1285
-
1286
-    /**
1287
-     * Returns a unique bean according to the filters passed in parameter.
1288
-     * Throws a NoBeanFoundException if no bean was found for the filter passed in parameter.
1289
-     *
1290
-     * @param string            $mainTable             The name of the table queried
1291
-     * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1292
-     * @param array             $parameters
1293
-     * @param array             $additionalTablesFetch
1294
-     * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1295
-     *
1296
-     * @return AbstractTDBMObject The object we want
1297
-     *
1298
-     * @throws TDBMException
1299
-     */
1300
-    public function findObjectOrFail(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1301
-    {
1302
-        $bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className);
1303
-        if ($bean === null) {
1304
-            throw new NoBeanFoundException("No result found for query on table '".$mainTable."'");
1305
-        }
1306
-
1307
-        return $bean;
1308
-    }
1309
-
1310
-    /**
1311
-     * @param array $beanData An array of data: array<table, array<column, value>>
1312
-     *
1313
-     * @return array an array with first item = class name, second item = table name and third item = list of tables needed
1314
-     *
1315
-     * @throws TDBMInheritanceException
1316
-     */
1317
-    public function _getClassNameFromBeanData(array $beanData)
1318
-    {
1319
-        if (count($beanData) === 1) {
1320
-            $tableName = array_keys($beanData)[0];
1321
-            $allTables = [$tableName];
1322
-        } else {
1323
-            $tables = [];
1324
-            foreach ($beanData as $table => $row) {
1325
-                $primaryKeyColumns = $this->getPrimaryKeyColumns($table);
1326
-                $pkSet = false;
1327
-                foreach ($primaryKeyColumns as $columnName) {
1328
-                    if ($row[$columnName] !== null) {
1329
-                        $pkSet = true;
1330
-                        break;
1331
-                    }
1332
-                }
1333
-                if ($pkSet) {
1334
-                    $tables[] = $table;
1335
-                }
1336
-            }
1337
-
1338
-            // $tables contains the tables for this bean. Let's view the top most part of the hierarchy
1339
-            try {
1340
-                $allTables = $this->_getLinkBetweenInheritedTables($tables);
1341
-            } catch (TDBMInheritanceException $e) {
1342
-                throw TDBMInheritanceException::extendException($e, $this, $beanData);
1343
-            }
1344
-            $tableName = $allTables[0];
1345
-        }
1346
-
1347
-        // Only one table in this bean. Life is sweat, let's look at its type:
1348
-        try {
1349
-            $className = $this->getBeanClassName($tableName);
1350
-        } catch (TDBMInvalidArgumentException $e) {
1351
-            $className = 'Mouf\\Database\\TDBM\\TDBMObject';
1352
-        }
1353
-
1354
-        return [$className, $tableName, $allTables];
1355
-    }
1356
-
1357
-    /**
1358
-     * Returns an item from cache or computes it using $closure and puts it in cache.
1359
-     *
1360
-     * @param string   $key
1361
-     * @param callable $closure
1362
-     *
1363
-     * @return mixed
1364
-     */
1365
-    private function fromCache(string $key, callable $closure)
1366
-    {
1367
-        $item = $this->cache->fetch($key);
1368
-        if ($item === false) {
1369
-            $item = $closure();
1370
-            $this->cache->save($key, $item);
1371
-        }
1372
-
1373
-        return $item;
1374
-    }
1375
-
1376
-    /**
1377
-     * Returns the foreign key object.
1378
-     *
1379
-     * @param string $table
1380
-     * @param string $fkName
1381
-     *
1382
-     * @return ForeignKeyConstraint
1383
-     */
1384
-    public function _getForeignKeyByName(string $table, string $fkName)
1385
-    {
1386
-        return $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getForeignKey($fkName);
1387
-    }
1388
-
1389
-    /**
1390
-     * @param $pivotTableName
1391
-     * @param AbstractTDBMObject $bean
1392
-     *
1393
-     * @return AbstractTDBMObject[]
1394
-     */
1395
-    public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean)
1396
-    {
1397
-        list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $bean);
1398
-        /* @var $localFk ForeignKeyConstraint */
1399
-        /* @var $remoteFk ForeignKeyConstraint */
1400
-        $remoteTable = $remoteFk->getForeignTableName();
1401
-
1402
-        $primaryKeys = $this->getPrimaryKeyValues($bean);
1403
-        $columnNames = array_map(function ($name) use ($pivotTableName) {
1404
-            return $pivotTableName.'.'.$name;
1405
-        }, $localFk->getLocalColumns());
1406
-
1407
-        $filter = array_combine($columnNames, $primaryKeys);
1408
-
1409
-        return $this->findObjects($remoteTable, $filter);
1410
-    }
1411
-
1412
-    /**
1413
-     * @param $pivotTableName
1414
-     * @param AbstractTDBMObject $bean The LOCAL bean
1415
-     *
1416
-     * @return ForeignKeyConstraint[] First item: the LOCAL bean, second item: the REMOTE bean
1417
-     *
1418
-     * @throws TDBMException
1419
-     */
1420
-    private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMObject $bean)
1421
-    {
1422
-        $fks = array_values($this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName)->getForeignKeys());
1423
-        $table1 = $fks[0]->getForeignTableName();
1424
-        $table2 = $fks[1]->getForeignTableName();
1425
-
1426
-        $beanTables = array_map(function (DbRow $dbRow) {
1427
-            return $dbRow->_getDbTableName();
1428
-        }, $bean->_getDbRows());
1429
-
1430
-        if (in_array($table1, $beanTables)) {
1431
-            return [$fks[0], $fks[1]];
1432
-        } elseif (in_array($table2, $beanTables)) {
1433
-            return [$fks[1], $fks[0]];
1434
-        } else {
1435
-            throw new TDBMException("Unexpected bean type in getPivotTableForeignKeys. Awaiting beans from table {$table1} and {$table2} for pivot table {$pivotTableName}");
1436
-        }
1437
-    }
1438
-
1439
-    /**
1440
-     * Returns a list of pivot tables linked to $bean.
1441
-     *
1442
-     * @param AbstractTDBMObject $bean
1443
-     *
1444
-     * @return string[]
1445
-     */
1446
-    public function _getPivotTablesLinkedToBean(AbstractTDBMObject $bean)
1447
-    {
1448
-        $junctionTables = [];
1449
-        $allJunctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
1450
-        foreach ($bean->_getDbRows() as $dbRow) {
1451
-            foreach ($allJunctionTables as $table) {
1452
-                // There are exactly 2 FKs since this is a pivot table.
1453
-                $fks = array_values($table->getForeignKeys());
1454
-
1455
-                if ($fks[0]->getForeignTableName() === $dbRow->_getDbTableName() || $fks[1]->getForeignTableName() === $dbRow->_getDbTableName()) {
1456
-                    $junctionTables[] = $table->getName();
1457
-                }
1458
-            }
1459
-        }
1460
-
1461
-        return $junctionTables;
1462
-    }
1463
-
1464
-    /**
1465
-     * Array of types for tables.
1466
-     * Key: table name
1467
-     * Value: array of types indexed by column.
1468
-     *
1469
-     * @var array[]
1470
-     */
1471
-    private $typesForTable = [];
1472
-
1473
-    /**
1474
-     * @internal
1475
-     *
1476
-     * @param string $tableName
1477
-     *
1478
-     * @return Type[]
1479
-     */
1480
-    public function _getColumnTypesForTable(string $tableName)
1481
-    {
1482
-        if (!isset($typesForTable[$tableName])) {
1483
-            $columns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getColumns();
1484
-            $typesForTable[$tableName] = array_map(function (Column $column) {
1485
-                return $column->getType();
1486
-            }, $columns);
1487
-        }
1488
-
1489
-        return $typesForTable[$tableName];
1490
-    }
1491
-
1492
-    /**
1493
-     * Sets the minimum log level.
1494
-     * $level must be one of Psr\Log\LogLevel::xxx.
1495
-     *
1496
-     * Defaults to LogLevel::WARNING
1497
-     *
1498
-     * @param string $level
1499
-     */
1500
-    public function setLogLevel(string $level)
1501
-    {
1502
-        $this->logger = new LevelFilter($this->rootLogger, $level);
1503
-    }
1091
+	/**
1092
+	 * Returns a `ResultIterator` object representing filtered records of "$mainTable" .
1093
+	 *
1094
+	 * The findObjects method should be the most used query method in TDBM if you want to query the database for objects.
1095
+	 * (Note: if you want to query the database for an object by its primary key, use the findObjectByPk method).
1096
+	 *
1097
+	 * The findObjects method takes in parameter:
1098
+	 * 	- mainTable: the kind of bean you want to retrieve. In TDBM, a bean matches a database row, so the
1099
+	 * 			`$mainTable` parameter should be the name of an existing table in database.
1100
+	 *  - filter: The filter is a filter bag. It is what you use to filter your request (the WHERE part in SQL).
1101
+	 *          It can be a string (SQL Where clause), or even a bean or an associative array (key = column to filter, value = value to find)
1102
+	 *  - parameters: The parameters used in the filter. If you pass a SQL string as a filter, be sure to avoid
1103
+	 *          concatenating parameters in the string (this leads to SQL injection and also to poor caching performance).
1104
+	 *          Instead, please consider passing parameters (see documentation for more details).
1105
+	 *  - additionalTablesFetch: An array of SQL tables names. The beans related to those tables will be fetched along
1106
+	 *          the main table. This is useful to avoid hitting the database with numerous subqueries.
1107
+	 *  - mode: The fetch mode of the result. See `setFetchMode()` method for more details.
1108
+	 *
1109
+	 * The `findObjects` method will return a `ResultIterator`. A `ResultIterator` is an object that behaves as an array
1110
+	 * (in ARRAY mode) at least. It can be iterated using a `foreach` loop.
1111
+	 *
1112
+	 * Finally, if filter_bag is null, the whole table is returned.
1113
+	 *
1114
+	 * @param string                       $mainTable             The name of the table queried
1115
+	 * @param string|array|null            $filter                The SQL filters to apply to the query (the WHERE part). Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1116
+	 * @param array                        $parameters
1117
+	 * @param string|UncheckedOrderBy|null $orderString           The ORDER BY part of the query. Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1118
+	 * @param array                        $additionalTablesFetch
1119
+	 * @param int                          $mode
1120
+	 * @param string                       $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1121
+	 *
1122
+	 * @return ResultIterator An object representing an array of results
1123
+	 *
1124
+	 * @throws TDBMException
1125
+	 */
1126
+	public function findObjects(string $mainTable, $filter = null, array $parameters = array(), $orderString = null, array $additionalTablesFetch = array(), $mode = null, string $className = null)
1127
+	{
1128
+		// $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1129
+		if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1130
+			throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1131
+		}
1132
+
1133
+		$mode = $mode ?: $this->mode;
1134
+
1135
+		list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1136
+
1137
+		$parameters = array_merge($parameters, $additionalParameters);
1138
+
1139
+		$queryFactory = new FindObjectsQueryFactory($mainTable, $additionalTablesFetch, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer);
1140
+
1141
+		return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1142
+	}
1143
+
1144
+	/**
1145
+	 * @param string                       $mainTable   The name of the table queried
1146
+	 * @param string                       $from        The from sql statement
1147
+	 * @param string|array|null            $filter      The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1148
+	 * @param array                        $parameters
1149
+	 * @param string|UncheckedOrderBy|null $orderString The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column)
1150
+	 * @param int                          $mode
1151
+	 * @param string                       $className   Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1152
+	 *
1153
+	 * @return ResultIterator An object representing an array of results
1154
+	 *
1155
+	 * @throws TDBMException
1156
+	 */
1157
+	public function findObjectsFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), $orderString = null, $mode = null, string $className = null)
1158
+	{
1159
+		// $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1160
+		if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1161
+			throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1162
+		}
1163
+
1164
+		$mode = $mode ?: $this->mode;
1165
+
1166
+		list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1167
+
1168
+		$parameters = array_merge($parameters, $additionalParameters);
1169
+
1170
+		$queryFactory = new FindObjectsFromSqlQueryFactory($mainTable, $from, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer, $this->schemaAnalyzer, $this->cache, $this->cachePrefix);
1171
+
1172
+		return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1173
+	}
1174
+
1175
+	/**
1176
+	 * @param $table
1177
+	 * @param array  $primaryKeys
1178
+	 * @param array  $additionalTablesFetch
1179
+	 * @param bool   $lazy                  Whether to perform lazy loading on this object or not
1180
+	 * @param string $className
1181
+	 *
1182
+	 * @return AbstractTDBMObject
1183
+	 *
1184
+	 * @throws TDBMException
1185
+	 */
1186
+	public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch = array(), bool $lazy = false, string $className = null)
1187
+	{
1188
+		$primaryKeys = $this->_getPrimaryKeysFromObjectData($table, $primaryKeys);
1189
+		$hash = $this->getObjectHash($primaryKeys);
1190
+
1191
+		if ($this->objectStorage->has($table, $hash)) {
1192
+			$dbRow = $this->objectStorage->get($table, $hash);
1193
+			$bean = $dbRow->getTDBMObject();
1194
+			if ($className !== null && !is_a($bean, $className)) {
1195
+				throw new TDBMException("TDBM cannot create a bean of class '".$className."'. The requested object was already loaded and its class is '".get_class($bean)."'");
1196
+			}
1197
+
1198
+			return $bean;
1199
+		}
1200
+
1201
+		// Are we performing lazy fetching?
1202
+		if ($lazy === true) {
1203
+			// Can we perform lazy fetching?
1204
+			$tables = $this->_getRelatedTablesByInheritance($table);
1205
+			// Only allowed if no inheritance.
1206
+			if (count($tables) === 1) {
1207
+				if ($className === null) {
1208
+					try {
1209
+						$className = $this->getBeanClassName($table);
1210
+					} catch (TDBMInvalidArgumentException $e) {
1211
+						$className = TDBMObject::class;
1212
+					}
1213
+				}
1214
+
1215
+				// Let's construct the bean
1216
+				if (!isset($this->reflectionClassCache[$className])) {
1217
+					$this->reflectionClassCache[$className] = new \ReflectionClass($className);
1218
+				}
1219
+				// Let's bypass the constructor when creating the bean!
1220
+				$bean = $this->reflectionClassCache[$className]->newInstanceWithoutConstructor();
1221
+				/* @var $bean AbstractTDBMObject */
1222
+				$bean->_constructLazy($table, $primaryKeys, $this);
1223
+
1224
+				return $bean;
1225
+			}
1226
+		}
1227
+
1228
+		// Did not find the object in cache? Let's query it!
1229
+		return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className);
1230
+	}
1231
+
1232
+	/**
1233
+	 * Returns a unique bean (or null) according to the filters passed in parameter.
1234
+	 *
1235
+	 * @param string            $mainTable             The name of the table queried
1236
+	 * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1237
+	 * @param array             $parameters
1238
+	 * @param array             $additionalTablesFetch
1239
+	 * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1240
+	 *
1241
+	 * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1242
+	 *
1243
+	 * @throws TDBMException
1244
+	 */
1245
+	public function findObject(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1246
+	{
1247
+		$objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className);
1248
+		$page = $objects->take(0, 2);
1249
+		$count = $page->count();
1250
+		if ($count > 1) {
1251
+			throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1252
+		} elseif ($count === 0) {
1253
+			return;
1254
+		}
1255
+
1256
+		return $page[0];
1257
+	}
1258
+
1259
+	/**
1260
+	 * Returns a unique bean (or null) according to the filters passed in parameter.
1261
+	 *
1262
+	 * @param string            $mainTable  The name of the table queried
1263
+	 * @param string            $from       The from sql statement
1264
+	 * @param string|array|null $filter     The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1265
+	 * @param array             $parameters
1266
+	 * @param string            $className  Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1267
+	 *
1268
+	 * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1269
+	 *
1270
+	 * @throws TDBMException
1271
+	 */
1272
+	public function findObjectFromSql($mainTable, $from, $filter = null, array $parameters = array(), $className = null)
1273
+	{
1274
+		$objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className);
1275
+		$page = $objects->take(0, 2);
1276
+		$count = $page->count();
1277
+		if ($count > 1) {
1278
+			throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1279
+		} elseif ($count === 0) {
1280
+			return;
1281
+		}
1282
+
1283
+		return $page[0];
1284
+	}
1285
+
1286
+	/**
1287
+	 * Returns a unique bean according to the filters passed in parameter.
1288
+	 * Throws a NoBeanFoundException if no bean was found for the filter passed in parameter.
1289
+	 *
1290
+	 * @param string            $mainTable             The name of the table queried
1291
+	 * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1292
+	 * @param array             $parameters
1293
+	 * @param array             $additionalTablesFetch
1294
+	 * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1295
+	 *
1296
+	 * @return AbstractTDBMObject The object we want
1297
+	 *
1298
+	 * @throws TDBMException
1299
+	 */
1300
+	public function findObjectOrFail(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1301
+	{
1302
+		$bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className);
1303
+		if ($bean === null) {
1304
+			throw new NoBeanFoundException("No result found for query on table '".$mainTable."'");
1305
+		}
1306
+
1307
+		return $bean;
1308
+	}
1309
+
1310
+	/**
1311
+	 * @param array $beanData An array of data: array<table, array<column, value>>
1312
+	 *
1313
+	 * @return array an array with first item = class name, second item = table name and third item = list of tables needed
1314
+	 *
1315
+	 * @throws TDBMInheritanceException
1316
+	 */
1317
+	public function _getClassNameFromBeanData(array $beanData)
1318
+	{
1319
+		if (count($beanData) === 1) {
1320
+			$tableName = array_keys($beanData)[0];
1321
+			$allTables = [$tableName];
1322
+		} else {
1323
+			$tables = [];
1324
+			foreach ($beanData as $table => $row) {
1325
+				$primaryKeyColumns = $this->getPrimaryKeyColumns($table);
1326
+				$pkSet = false;
1327
+				foreach ($primaryKeyColumns as $columnName) {
1328
+					if ($row[$columnName] !== null) {
1329
+						$pkSet = true;
1330
+						break;
1331
+					}
1332
+				}
1333
+				if ($pkSet) {
1334
+					$tables[] = $table;
1335
+				}
1336
+			}
1337
+
1338
+			// $tables contains the tables for this bean. Let's view the top most part of the hierarchy
1339
+			try {
1340
+				$allTables = $this->_getLinkBetweenInheritedTables($tables);
1341
+			} catch (TDBMInheritanceException $e) {
1342
+				throw TDBMInheritanceException::extendException($e, $this, $beanData);
1343
+			}
1344
+			$tableName = $allTables[0];
1345
+		}
1346
+
1347
+		// Only one table in this bean. Life is sweat, let's look at its type:
1348
+		try {
1349
+			$className = $this->getBeanClassName($tableName);
1350
+		} catch (TDBMInvalidArgumentException $e) {
1351
+			$className = 'Mouf\\Database\\TDBM\\TDBMObject';
1352
+		}
1353
+
1354
+		return [$className, $tableName, $allTables];
1355
+	}
1356
+
1357
+	/**
1358
+	 * Returns an item from cache or computes it using $closure and puts it in cache.
1359
+	 *
1360
+	 * @param string   $key
1361
+	 * @param callable $closure
1362
+	 *
1363
+	 * @return mixed
1364
+	 */
1365
+	private function fromCache(string $key, callable $closure)
1366
+	{
1367
+		$item = $this->cache->fetch($key);
1368
+		if ($item === false) {
1369
+			$item = $closure();
1370
+			$this->cache->save($key, $item);
1371
+		}
1372
+
1373
+		return $item;
1374
+	}
1375
+
1376
+	/**
1377
+	 * Returns the foreign key object.
1378
+	 *
1379
+	 * @param string $table
1380
+	 * @param string $fkName
1381
+	 *
1382
+	 * @return ForeignKeyConstraint
1383
+	 */
1384
+	public function _getForeignKeyByName(string $table, string $fkName)
1385
+	{
1386
+		return $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getForeignKey($fkName);
1387
+	}
1388
+
1389
+	/**
1390
+	 * @param $pivotTableName
1391
+	 * @param AbstractTDBMObject $bean
1392
+	 *
1393
+	 * @return AbstractTDBMObject[]
1394
+	 */
1395
+	public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean)
1396
+	{
1397
+		list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $bean);
1398
+		/* @var $localFk ForeignKeyConstraint */
1399
+		/* @var $remoteFk ForeignKeyConstraint */
1400
+		$remoteTable = $remoteFk->getForeignTableName();
1401
+
1402
+		$primaryKeys = $this->getPrimaryKeyValues($bean);
1403
+		$columnNames = array_map(function ($name) use ($pivotTableName) {
1404
+			return $pivotTableName.'.'.$name;
1405
+		}, $localFk->getLocalColumns());
1406
+
1407
+		$filter = array_combine($columnNames, $primaryKeys);
1408
+
1409
+		return $this->findObjects($remoteTable, $filter);
1410
+	}
1411
+
1412
+	/**
1413
+	 * @param $pivotTableName
1414
+	 * @param AbstractTDBMObject $bean The LOCAL bean
1415
+	 *
1416
+	 * @return ForeignKeyConstraint[] First item: the LOCAL bean, second item: the REMOTE bean
1417
+	 *
1418
+	 * @throws TDBMException
1419
+	 */
1420
+	private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMObject $bean)
1421
+	{
1422
+		$fks = array_values($this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName)->getForeignKeys());
1423
+		$table1 = $fks[0]->getForeignTableName();
1424
+		$table2 = $fks[1]->getForeignTableName();
1425
+
1426
+		$beanTables = array_map(function (DbRow $dbRow) {
1427
+			return $dbRow->_getDbTableName();
1428
+		}, $bean->_getDbRows());
1429
+
1430
+		if (in_array($table1, $beanTables)) {
1431
+			return [$fks[0], $fks[1]];
1432
+		} elseif (in_array($table2, $beanTables)) {
1433
+			return [$fks[1], $fks[0]];
1434
+		} else {
1435
+			throw new TDBMException("Unexpected bean type in getPivotTableForeignKeys. Awaiting beans from table {$table1} and {$table2} for pivot table {$pivotTableName}");
1436
+		}
1437
+	}
1438
+
1439
+	/**
1440
+	 * Returns a list of pivot tables linked to $bean.
1441
+	 *
1442
+	 * @param AbstractTDBMObject $bean
1443
+	 *
1444
+	 * @return string[]
1445
+	 */
1446
+	public function _getPivotTablesLinkedToBean(AbstractTDBMObject $bean)
1447
+	{
1448
+		$junctionTables = [];
1449
+		$allJunctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
1450
+		foreach ($bean->_getDbRows() as $dbRow) {
1451
+			foreach ($allJunctionTables as $table) {
1452
+				// There are exactly 2 FKs since this is a pivot table.
1453
+				$fks = array_values($table->getForeignKeys());
1454
+
1455
+				if ($fks[0]->getForeignTableName() === $dbRow->_getDbTableName() || $fks[1]->getForeignTableName() === $dbRow->_getDbTableName()) {
1456
+					$junctionTables[] = $table->getName();
1457
+				}
1458
+			}
1459
+		}
1460
+
1461
+		return $junctionTables;
1462
+	}
1463
+
1464
+	/**
1465
+	 * Array of types for tables.
1466
+	 * Key: table name
1467
+	 * Value: array of types indexed by column.
1468
+	 *
1469
+	 * @var array[]
1470
+	 */
1471
+	private $typesForTable = [];
1472
+
1473
+	/**
1474
+	 * @internal
1475
+	 *
1476
+	 * @param string $tableName
1477
+	 *
1478
+	 * @return Type[]
1479
+	 */
1480
+	public function _getColumnTypesForTable(string $tableName)
1481
+	{
1482
+		if (!isset($typesForTable[$tableName])) {
1483
+			$columns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getColumns();
1484
+			$typesForTable[$tableName] = array_map(function (Column $column) {
1485
+				return $column->getType();
1486
+			}, $columns);
1487
+		}
1488
+
1489
+		return $typesForTable[$tableName];
1490
+	}
1491
+
1492
+	/**
1493
+	 * Sets the minimum log level.
1494
+	 * $level must be one of Psr\Log\LogLevel::xxx.
1495
+	 *
1496
+	 * Defaults to LogLevel::WARNING
1497
+	 *
1498
+	 * @param string $level
1499
+	 */
1500
+	public function setLogLevel(string $level)
1501
+	{
1502
+		$this->logger = new LevelFilter($this->rootLogger, $level);
1503
+	}
1504 1504
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Controllers/TdbmController.php 1 patch
Indentation   +130 added lines, -130 removed lines patch added patch discarded remove patch
@@ -19,134 +19,134 @@
 block discarded – undo
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
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Utils/TDBMDaoGenerator.php 1 patch
Indentation   +362 added lines, -362 removed lines patch added patch discarded remove patch
@@ -16,135 +16,135 @@  discard block
 block discarded – undo
16 16
  */
17 17
 class TDBMDaoGenerator
18 18
 {
19
-    /**
20
-     * @var Schema
21
-     */
22
-    private $schema;
23
-
24
-    /**
25
-     * Name of composer file.
26
-     *
27
-     * @var string
28
-     */
29
-    private $composerFile;
30
-
31
-    /**
32
-     * @var TDBMSchemaAnalyzer
33
-     */
34
-    private $tdbmSchemaAnalyzer;
35
-
36
-    /**
37
-     * @var GeneratorListenerInterface
38
-     */
39
-    private $eventDispatcher;
40
-
41
-    /**
42
-     * @var NamingStrategyInterface
43
-     */
44
-    private $namingStrategy;
45
-    /**
46
-     * @var ConfigurationInterface
47
-     */
48
-    private $configuration;
49
-
50
-    /**
51
-     * Constructor.
52
-     *
53
-     * @param ConfigurationInterface $configuration
54
-     * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer
55
-     */
56
-    public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
57
-    {
58
-        $this->configuration = $configuration;
59
-        $this->schema = $tdbmSchemaAnalyzer->getSchema();
60
-        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
61
-        $this->namingStrategy = $configuration->getNamingStrategy();
62
-        $this->eventDispatcher = $configuration->getGeneratorEventDispatcher();
63
-    }
64
-
65
-    /**
66
-     * Generates all the daos and beans.
67
-     *
68
-     * @throws TDBMException
69
-     */
70
-    public function generateAllDaosAndBeans(): void
71
-    {
72
-        // TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
73
-
74
-        $tableList = $this->schema->getTables();
75
-
76
-        // Remove all beans and daos from junction tables
77
-        $junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true);
78
-        $junctionTableNames = array_map(function (Table $table) {
79
-            return $table->getName();
80
-        }, $junctionTables);
81
-
82
-        $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
83
-            return !in_array($table->getName(), $junctionTableNames);
84
-        });
85
-
86
-        $beanDescriptors = [];
87
-
88
-        foreach ($tableList as $table) {
89
-            $beanDescriptors[] = $this->generateDaoAndBean($table);
90
-        }
91
-
92
-
93
-        $this->generateFactory($tableList);
94
-
95
-        // Let's call the list of listeners
96
-        $this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors);
97
-    }
98
-
99
-    /**
100
-     * Generates in one method call the daos and the beans for one table.
101
-     *
102
-     * @param Table $table
103
-     *
104
-     * @return BeanDescriptor
105
-     * @throws TDBMException
106
-     */
107
-    private function generateDaoAndBean(Table $table) : BeanDescriptor
108
-    {
109
-        // TODO: $storeInUtc is NOT USED.
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->ensureDirectoryExist($possibleBaseFileName);
140
-        file_put_contents($possibleBaseFileName, $str);
141
-        @chmod($possibleBaseFileName, 0664);
142
-
143
-        $possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname();
144
-
145
-        if (!file_exists($possibleFileName)) {
146
-            $tableName = $table->getName();
147
-            $str = "<?php
19
+	/**
20
+	 * @var Schema
21
+	 */
22
+	private $schema;
23
+
24
+	/**
25
+	 * Name of composer file.
26
+	 *
27
+	 * @var string
28
+	 */
29
+	private $composerFile;
30
+
31
+	/**
32
+	 * @var TDBMSchemaAnalyzer
33
+	 */
34
+	private $tdbmSchemaAnalyzer;
35
+
36
+	/**
37
+	 * @var GeneratorListenerInterface
38
+	 */
39
+	private $eventDispatcher;
40
+
41
+	/**
42
+	 * @var NamingStrategyInterface
43
+	 */
44
+	private $namingStrategy;
45
+	/**
46
+	 * @var ConfigurationInterface
47
+	 */
48
+	private $configuration;
49
+
50
+	/**
51
+	 * Constructor.
52
+	 *
53
+	 * @param ConfigurationInterface $configuration
54
+	 * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer
55
+	 */
56
+	public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
57
+	{
58
+		$this->configuration = $configuration;
59
+		$this->schema = $tdbmSchemaAnalyzer->getSchema();
60
+		$this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
61
+		$this->namingStrategy = $configuration->getNamingStrategy();
62
+		$this->eventDispatcher = $configuration->getGeneratorEventDispatcher();
63
+	}
64
+
65
+	/**
66
+	 * Generates all the daos and beans.
67
+	 *
68
+	 * @throws TDBMException
69
+	 */
70
+	public function generateAllDaosAndBeans(): void
71
+	{
72
+		// TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
73
+
74
+		$tableList = $this->schema->getTables();
75
+
76
+		// Remove all beans and daos from junction tables
77
+		$junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true);
78
+		$junctionTableNames = array_map(function (Table $table) {
79
+			return $table->getName();
80
+		}, $junctionTables);
81
+
82
+		$tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
83
+			return !in_array($table->getName(), $junctionTableNames);
84
+		});
85
+
86
+		$beanDescriptors = [];
87
+
88
+		foreach ($tableList as $table) {
89
+			$beanDescriptors[] = $this->generateDaoAndBean($table);
90
+		}
91
+
92
+
93
+		$this->generateFactory($tableList);
94
+
95
+		// Let's call the list of listeners
96
+		$this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors);
97
+	}
98
+
99
+	/**
100
+	 * Generates in one method call the daos and the beans for one table.
101
+	 *
102
+	 * @param Table $table
103
+	 *
104
+	 * @return BeanDescriptor
105
+	 * @throws TDBMException
106
+	 */
107
+	private function generateDaoAndBean(Table $table) : BeanDescriptor
108
+	{
109
+		// TODO: $storeInUtc is NOT USED.
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->ensureDirectoryExist($possibleBaseFileName);
140
+		file_put_contents($possibleBaseFileName, $str);
141
+		@chmod($possibleBaseFileName, 0664);
142
+
143
+		$possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname();
144
+
145
+		if (!file_exists($possibleFileName)) {
146
+			$tableName = $table->getName();
147
+			$str = "<?php
148 148
 /*
149 149
  * This file has been automatically generated by TDBM.
150 150
  * You can edit this file as it will not be overwritten.
@@ -161,75 +161,75 @@  discard block
 block discarded – undo
161 161
 {
162 162
 }
163 163
 ";
164
-            $this->ensureDirectoryExist($possibleFileName);
165
-            file_put_contents($possibleFileName, $str);
166
-            @chmod($possibleFileName, 0664);
167
-        }
168
-    }
169
-
170
-    /**
171
-     * Tries to find a @defaultSort annotation in one of the columns.
172
-     *
173
-     * @param Table $table
174
-     *
175
-     * @return array First item: column name, Second item: column order (asc/desc)
176
-     */
177
-    private function getDefaultSortColumnFromAnnotation(Table $table)
178
-    {
179
-        $defaultSort = null;
180
-        $defaultSortDirection = null;
181
-        foreach ($table->getColumns() as $column) {
182
-            $comments = $column->getComment();
183
-            $matches = [];
184
-            if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
185
-                $defaultSort = $column->getName();
186
-                if (count($matches) === 3) {
187
-                    $defaultSortDirection = $matches[2];
188
-                } else {
189
-                    $defaultSortDirection = 'ASC';
190
-                }
191
-            }
192
-        }
193
-
194
-        return [$defaultSort, $defaultSortDirection];
195
-    }
196
-
197
-    /**
198
-     * Writes the PHP bean DAO with simple functions to create/get/save objects.
199
-     *
200
-     * @param BeanDescriptor  $beanDescriptor
201
-     * @param string          $className       The name of the class
202
-     * @param string          $baseClassName
203
-     * @param string          $beanClassName
204
-     * @param Table           $table
205
-     *
206
-     * @throws TDBMException
207
-     */
208
-    private function generateDao(BeanDescriptor $beanDescriptor, string $className, string $baseClassName, string $beanClassName, Table $table)
209
-    {
210
-        $daonamespace = $this->configuration->getDaoNamespace();
211
-        $beannamespace = $this->configuration->getBeanNamespace();
212
-        $tableName = $table->getName();
213
-        $primaryKeyColumns = $table->getPrimaryKeyColumns();
214
-
215
-        list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table);
216
-
217
-        // FIXME: lowercase tables with _ in the name should work!
218
-        $tableCamel = self::toSingular(self::toCamelCase($tableName));
219
-
220
-        $beanClassWithoutNameSpace = $beanClassName;
221
-        $beanClassName = $beannamespace.'\\'.$beanClassName;
222
-
223
-        list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace);
224
-
225
-        $usedBeans[] = $beanClassName;
226
-        // Let's suppress duplicates in used beans (if any)
227
-        $usedBeans = array_flip(array_flip($usedBeans));
228
-        $useStatements = array_map(function ($usedBean) {
229
-            return "use $usedBean;\n";
230
-        }, $usedBeans);
231
-
232
-        $str = "<?php
164
+			$this->ensureDirectoryExist($possibleFileName);
165
+			file_put_contents($possibleFileName, $str);
166
+			@chmod($possibleFileName, 0664);
167
+		}
168
+	}
169
+
170
+	/**
171
+	 * Tries to find a @defaultSort annotation in one of the columns.
172
+	 *
173
+	 * @param Table $table
174
+	 *
175
+	 * @return array First item: column name, Second item: column order (asc/desc)
176
+	 */
177
+	private function getDefaultSortColumnFromAnnotation(Table $table)
178
+	{
179
+		$defaultSort = null;
180
+		$defaultSortDirection = null;
181
+		foreach ($table->getColumns() as $column) {
182
+			$comments = $column->getComment();
183
+			$matches = [];
184
+			if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
185
+				$defaultSort = $column->getName();
186
+				if (count($matches) === 3) {
187
+					$defaultSortDirection = $matches[2];
188
+				} else {
189
+					$defaultSortDirection = 'ASC';
190
+				}
191
+			}
192
+		}
193
+
194
+		return [$defaultSort, $defaultSortDirection];
195
+	}
196
+
197
+	/**
198
+	 * Writes the PHP bean DAO with simple functions to create/get/save objects.
199
+	 *
200
+	 * @param BeanDescriptor  $beanDescriptor
201
+	 * @param string          $className       The name of the class
202
+	 * @param string          $baseClassName
203
+	 * @param string          $beanClassName
204
+	 * @param Table           $table
205
+	 *
206
+	 * @throws TDBMException
207
+	 */
208
+	private function generateDao(BeanDescriptor $beanDescriptor, string $className, string $baseClassName, string $beanClassName, Table $table)
209
+	{
210
+		$daonamespace = $this->configuration->getDaoNamespace();
211
+		$beannamespace = $this->configuration->getBeanNamespace();
212
+		$tableName = $table->getName();
213
+		$primaryKeyColumns = $table->getPrimaryKeyColumns();
214
+
215
+		list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table);
216
+
217
+		// FIXME: lowercase tables with _ in the name should work!
218
+		$tableCamel = self::toSingular(self::toCamelCase($tableName));
219
+
220
+		$beanClassWithoutNameSpace = $beanClassName;
221
+		$beanClassName = $beannamespace.'\\'.$beanClassName;
222
+
223
+		list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace);
224
+
225
+		$usedBeans[] = $beanClassName;
226
+		// Let's suppress duplicates in used beans (if any)
227
+		$usedBeans = array_flip(array_flip($usedBeans));
228
+		$useStatements = array_map(function ($usedBean) {
229
+			return "use $usedBean;\n";
230
+		}, $usedBeans);
231
+
232
+		$str = "<?php
233 233
 
234 234
 /*
235 235
  * This file has been automatically generated by TDBM.
@@ -305,10 +305,10 @@  discard block
 block discarded – undo
305 305
     }
306 306
     ";
307 307
 
308
-        if (count($primaryKeyColumns) === 1) {
309
-            $primaryKeyColumn = $primaryKeyColumns[0];
310
-            $primaryKeyPhpType = self::dbalTypeToPhpType($table->getColumn($primaryKeyColumn)->getType());
311
-            $str .= "
308
+		if (count($primaryKeyColumns) === 1) {
309
+			$primaryKeyColumn = $primaryKeyColumns[0];
310
+			$primaryKeyPhpType = self::dbalTypeToPhpType($table->getColumn($primaryKeyColumn)->getType());
311
+			$str .= "
312 312
     /**
313 313
      * Get $beanClassWithoutNameSpace specified by its ID (its primary key)
314 314
      * If the primary key does not exist, an exception is thrown.
@@ -323,8 +323,8 @@  discard block
 block discarded – undo
323 323
         return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);
324 324
     }
325 325
     ";
326
-        }
327
-        $str .= "
326
+		}
327
+		$str .= "
328 328
     /**
329 329
      * Deletes the $beanClassWithoutNameSpace passed in parameter.
330 330
      *
@@ -424,21 +424,21 @@  discard block
 block discarded – undo
424 424
     }
425 425
 ";
426 426
 
427
-        $str .= $findByDaoCode;
428
-        $str .= '}
427
+		$str .= $findByDaoCode;
428
+		$str .= '}
429 429
 ';
430 430
 
431
-        $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname();
431
+		$possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname();
432 432
 
433
-        $this->ensureDirectoryExist($possibleBaseFileName);
434
-        file_put_contents($possibleBaseFileName, $str);
435
-        @chmod($possibleBaseFileName, 0664);
433
+		$this->ensureDirectoryExist($possibleBaseFileName);
434
+		file_put_contents($possibleBaseFileName, $str);
435
+		@chmod($possibleBaseFileName, 0664);
436 436
 
437
-        $possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname();
437
+		$possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname();
438 438
 
439
-        // Now, let's generate the "editable" class
440
-        if (!file_exists($possibleFileName)) {
441
-            $str = "<?php
439
+		// Now, let's generate the "editable" class
440
+		if (!file_exists($possibleFileName)) {
441
+			$str = "<?php
442 442
 
443 443
 /*
444 444
  * This file has been automatically generated by TDBM.
@@ -456,26 +456,26 @@  discard block
 block discarded – undo
456 456
 {
457 457
 }
458 458
 ";
459
-            $this->ensureDirectoryExist($possibleFileName);
460
-            file_put_contents($possibleFileName, $str);
461
-            @chmod($possibleFileName, 0664);
462
-        }
463
-    }
464
-
465
-    /**
466
-     * Generates the factory bean.
467
-     *
468
-     * @param Table[] $tableList
469
-     * @throws TDBMException
470
-     */
471
-    private function generateFactory(array $tableList) : void
472
-    {
473
-        $daoNamespace = $this->configuration->getDaoNamespace();
474
-        $daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName();
475
-
476
-        // For each table, let's write a property.
477
-
478
-        $str = "<?php
459
+			$this->ensureDirectoryExist($possibleFileName);
460
+			file_put_contents($possibleFileName, $str);
461
+			@chmod($possibleFileName, 0664);
462
+		}
463
+	}
464
+
465
+	/**
466
+	 * Generates the factory bean.
467
+	 *
468
+	 * @param Table[] $tableList
469
+	 * @throws TDBMException
470
+	 */
471
+	private function generateFactory(array $tableList) : void
472
+	{
473
+		$daoNamespace = $this->configuration->getDaoNamespace();
474
+		$daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName();
475
+
476
+		// For each table, let's write a property.
477
+
478
+		$str = "<?php
479 479
 
480 480
 /*
481 481
  * This file has been automatically generated by TDBM.
@@ -485,13 +485,13 @@  discard block
 block discarded – undo
485 485
 namespace {$daoNamespace}\\Generated;
486 486
 
487 487
 ";
488
-        foreach ($tableList as $table) {
489
-            $tableName = $table->getName();
490
-            $daoClassName = $this->namingStrategy->getDaoClassName($tableName);
491
-            $str .= "use {$daoNamespace}\\".$daoClassName.";\n";
492
-        }
488
+		foreach ($tableList as $table) {
489
+			$tableName = $table->getName();
490
+			$daoClassName = $this->namingStrategy->getDaoClassName($tableName);
491
+			$str .= "use {$daoNamespace}\\".$daoClassName.";\n";
492
+		}
493 493
 
494
-        $str .= "
494
+		$str .= "
495 495
 /**
496 496
  * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.
497 497
  *
@@ -500,12 +500,12 @@  discard block
 block discarded – undo
500 500
 {
501 501
 ";
502 502
 
503
-        foreach ($tableList as $table) {
504
-            $tableName = $table->getName();
505
-            $daoClassName = $this->namingStrategy->getDaoClassName($tableName);
506
-            $daoInstanceName = self::toVariableName($daoClassName);
503
+		foreach ($tableList as $table) {
504
+			$tableName = $table->getName();
505
+			$daoClassName = $this->namingStrategy->getDaoClassName($tableName);
506
+			$daoInstanceName = self::toVariableName($daoClassName);
507 507
 
508
-            $str .= '    /**
508
+			$str .= '    /**
509 509
      * @var '.$daoClassName.'
510 510
      */
511 511
     private $'.$daoInstanceName.';
@@ -529,125 +529,125 @@  discard block
 block discarded – undo
529 529
     {
530 530
         $this->'.$daoInstanceName.' = $'.$daoInstanceName.';
531 531
     }';
532
-        }
532
+		}
533 533
 
534
-        $str .= '
534
+		$str .= '
535 535
 }
536 536
 ';
537 537
 
538
-        $possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname();
539
-
540
-        $this->ensureDirectoryExist($possibleFileName);
541
-        file_put_contents($possibleFileName, $str);
542
-        @chmod($possibleFileName, 0664);
543
-    }
544
-
545
-    /**
546
-     * Transforms a string to camelCase (except the first letter will be uppercase too).
547
-     * Underscores and spaces are removed and the first letter after the underscore is uppercased.
548
-     *
549
-     * @param $str string
550
-     *
551
-     * @return string
552
-     */
553
-    public static function toCamelCase($str)
554
-    {
555
-        $str = strtoupper(substr($str, 0, 1)).substr($str, 1);
556
-        while (true) {
557
-            if (strpos($str, '_') === false && strpos($str, ' ') === false) {
558
-                break;
559
-            }
560
-
561
-            $pos = strpos($str, '_');
562
-            if ($pos === false) {
563
-                $pos = strpos($str, ' ');
564
-            }
565
-            $before = substr($str, 0, $pos);
566
-            $after = substr($str, $pos + 1);
567
-            $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
568
-        }
569
-
570
-        return $str;
571
-    }
572
-
573
-    /**
574
-     * Tries to put string to the singular form (if it is plural).
575
-     * We assume the table names are in english.
576
-     *
577
-     * @param $str string
578
-     *
579
-     * @return string
580
-     */
581
-    public static function toSingular($str)
582
-    {
583
-        return Inflector::singularize($str);
584
-    }
585
-
586
-    /**
587
-     * Put the first letter of the string in lower case.
588
-     * Very useful to transform a class name into a variable name.
589
-     *
590
-     * @param $str string
591
-     *
592
-     * @return string
593
-     */
594
-    public static function toVariableName($str)
595
-    {
596
-        return strtolower(substr($str, 0, 1)).substr($str, 1);
597
-    }
598
-
599
-    /**
600
-     * Ensures the file passed in parameter can be written in its directory.
601
-     *
602
-     * @param string $fileName
603
-     *
604
-     * @throws TDBMException
605
-     */
606
-    private function ensureDirectoryExist($fileName)
607
-    {
608
-        $dirName = dirname($fileName);
609
-        if (!file_exists($dirName)) {
610
-            $old = umask(0);
611
-            $result = mkdir($dirName, 0775, true);
612
-            umask($old);
613
-            if ($result === false) {
614
-                throw new TDBMException("Unable to create directory: '".$dirName."'.");
615
-            }
616
-        }
617
-    }
618
-
619
-    /**
620
-     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
621
-     *
622
-     * @param Type $type The DBAL type
623
-     *
624
-     * @return string The PHP type
625
-     */
626
-    public static function dbalTypeToPhpType(Type $type)
627
-    {
628
-        $map = [
629
-            Type::TARRAY => 'array',
630
-            Type::SIMPLE_ARRAY => 'array',
631
-            'json' => 'array',  // 'json' is supported from Doctrine DBAL 2.6 only.
632
-            Type::JSON_ARRAY => 'array',
633
-            Type::BIGINT => 'string',
634
-            Type::BOOLEAN => 'bool',
635
-            Type::DATETIME => '\DateTimeInterface',
636
-            Type::DATETIMETZ => '\DateTimeInterface',
637
-            Type::DATE => '\DateTimeInterface',
638
-            Type::TIME => '\DateTimeInterface',
639
-            Type::DECIMAL => 'float',
640
-            Type::INTEGER => 'int',
641
-            Type::OBJECT => 'string',
642
-            Type::SMALLINT => 'int',
643
-            Type::STRING => 'string',
644
-            Type::TEXT => 'string',
645
-            Type::BINARY => 'string',
646
-            Type::BLOB => 'string',
647
-            Type::FLOAT => 'float',
648
-            Type::GUID => 'string',
649
-        ];
650
-
651
-        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
652
-    }
538
+		$possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname();
539
+
540
+		$this->ensureDirectoryExist($possibleFileName);
541
+		file_put_contents($possibleFileName, $str);
542
+		@chmod($possibleFileName, 0664);
543
+	}
544
+
545
+	/**
546
+	 * Transforms a string to camelCase (except the first letter will be uppercase too).
547
+	 * Underscores and spaces are removed and the first letter after the underscore is uppercased.
548
+	 *
549
+	 * @param $str string
550
+	 *
551
+	 * @return string
552
+	 */
553
+	public static function toCamelCase($str)
554
+	{
555
+		$str = strtoupper(substr($str, 0, 1)).substr($str, 1);
556
+		while (true) {
557
+			if (strpos($str, '_') === false && strpos($str, ' ') === false) {
558
+				break;
559
+			}
560
+
561
+			$pos = strpos($str, '_');
562
+			if ($pos === false) {
563
+				$pos = strpos($str, ' ');
564
+			}
565
+			$before = substr($str, 0, $pos);
566
+			$after = substr($str, $pos + 1);
567
+			$str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
568
+		}
569
+
570
+		return $str;
571
+	}
572
+
573
+	/**
574
+	 * Tries to put string to the singular form (if it is plural).
575
+	 * We assume the table names are in english.
576
+	 *
577
+	 * @param $str string
578
+	 *
579
+	 * @return string
580
+	 */
581
+	public static function toSingular($str)
582
+	{
583
+		return Inflector::singularize($str);
584
+	}
585
+
586
+	/**
587
+	 * Put the first letter of the string in lower case.
588
+	 * Very useful to transform a class name into a variable name.
589
+	 *
590
+	 * @param $str string
591
+	 *
592
+	 * @return string
593
+	 */
594
+	public static function toVariableName($str)
595
+	{
596
+		return strtolower(substr($str, 0, 1)).substr($str, 1);
597
+	}
598
+
599
+	/**
600
+	 * Ensures the file passed in parameter can be written in its directory.
601
+	 *
602
+	 * @param string $fileName
603
+	 *
604
+	 * @throws TDBMException
605
+	 */
606
+	private function ensureDirectoryExist($fileName)
607
+	{
608
+		$dirName = dirname($fileName);
609
+		if (!file_exists($dirName)) {
610
+			$old = umask(0);
611
+			$result = mkdir($dirName, 0775, true);
612
+			umask($old);
613
+			if ($result === false) {
614
+				throw new TDBMException("Unable to create directory: '".$dirName."'.");
615
+			}
616
+		}
617
+	}
618
+
619
+	/**
620
+	 * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
621
+	 *
622
+	 * @param Type $type The DBAL type
623
+	 *
624
+	 * @return string The PHP type
625
+	 */
626
+	public static function dbalTypeToPhpType(Type $type)
627
+	{
628
+		$map = [
629
+			Type::TARRAY => 'array',
630
+			Type::SIMPLE_ARRAY => 'array',
631
+			'json' => 'array',  // 'json' is supported from Doctrine DBAL 2.6 only.
632
+			Type::JSON_ARRAY => 'array',
633
+			Type::BIGINT => 'string',
634
+			Type::BOOLEAN => 'bool',
635
+			Type::DATETIME => '\DateTimeInterface',
636
+			Type::DATETIMETZ => '\DateTimeInterface',
637
+			Type::DATE => '\DateTimeInterface',
638
+			Type::TIME => '\DateTimeInterface',
639
+			Type::DECIMAL => 'float',
640
+			Type::INTEGER => 'int',
641
+			Type::OBJECT => 'string',
642
+			Type::SMALLINT => 'int',
643
+			Type::STRING => 'string',
644
+			Type::TEXT => 'string',
645
+			Type::BINARY => 'string',
646
+			Type::BLOB => 'string',
647
+			Type::FLOAT => 'float',
648
+			Type::GUID => 'string',
649
+		];
650
+
651
+		return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
652
+	}
653 653
 }
Please login to merge, or discard this patch.