PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/codeception/codeception/src/Codeception/Module/Doctrine2.php

https://gitlab.com/jhonn/rest
PHP | 346 lines | 175 code | 31 blank | 140 comment | 24 complexity | ade87ed4d9d12344f1be01f1730c5785 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. namespace Codeception\Module;
  3. /**
  4. * Allows integration and testing for projects with Doctrine2 ORM.
  5. *
  6. * Doctrine2 uses EntityManager to perform all database operations.
  7. * As the module uses active connection and active entity manager, instance of this object should be passed to this module.
  8. *
  9. * It can be done in bootstrap file, by setting static $em property:
  10. *
  11. * ``` php
  12. * <?php
  13. *
  14. * \Codeception\Module\Doctrine2::$em = $em
  15. *
  16. * ```
  17. * ## Status
  18. *
  19. * * Maintainer: **davert**
  20. * * Stability: **stable**
  21. * * Contact: codecept@davert.mail.ua
  22. *
  23. * ## Config
  24. *
  25. * * auto_connect: true - tries to get EntityManager through connected frameworks. If none found expects the $em values specified as described above.
  26. * * cleanup: true - all doctrine queries will be run in transaction, which will be rolled back at the end of test.
  27. *
  28. * ### Example (`functional.suite.yml`)
  29. *
  30. * modules:
  31. * enabled: [Doctrine2]
  32. * config:
  33. * Doctrine2:
  34. * cleanup: false
  35. */
  36. class Doctrine2 extends \Codeception\Module
  37. {
  38. protected $config = array('cleanup' => true, 'auto_connect' => true);
  39. /**
  40. * @var \Doctrine\ORM\EntityManager
  41. */
  42. public static $em = null;
  43. public function _before(\Codeception\TestCase $test)
  44. {
  45. // trying to connect to Symfony2 and get event manager
  46. if ($this->config['auto_connect']) {
  47. if ($this->hasModule('Symfony2')) {
  48. $symfonyModule = $this->getModule('Symfony2');
  49. $kernel = $symfonyModule->kernel;
  50. if ($kernel->getContainer()->has('doctrine')) {
  51. self::$em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
  52. $symfonyModule->client->persistentServices[] = 'doctrine.orm.entity_manager';
  53. $symfonyModule->client->persistentServices[] = 'doctrine.orm.default_entity_manager';
  54. }
  55. }
  56. if ($this->hasModule('ZF2')) {
  57. $zf2Module = $this->getModule('ZF2');
  58. $application = $zf2Module->application;
  59. $serviceLocator = $application->getServiceManager();
  60. if ($entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager')) {
  61. self::$em = $entityManager;
  62. }
  63. }
  64. }
  65. if (!self::$em) throw new \Codeception\Exception\ModuleConfig(__CLASS__,
  66. "Doctrine2 module requires EntityManager explicitly set.\n" .
  67. "You can use your bootstrap file to assign the EntityManager:\n\n" .
  68. '\Codeception\Module\Doctrine2::$em = $em');
  69. if (!self::$em instanceof \Doctrine\ORM\EntityManager) throw new \Codeception\Exception\ModuleConfig(__CLASS__,
  70. "Entity Manager was not properly set.\n" .
  71. "You can use your bootstrap file to assign the EntityManager:\n\n" .
  72. '\Codeception\Module\Doctrine2::$em = $em');
  73. self::$em->getConnection()->connect();
  74. if ($this->config['cleanup']) {
  75. self::$em->getConnection()->beginTransaction();
  76. }
  77. }
  78. public function _after(\Codeception\TestCase $test)
  79. {
  80. if (!self::$em) throw new \Codeception\Exception\ModuleConfig(__CLASS__,
  81. "Doctrine2 module requires EntityManager explicitly set.\n" .
  82. "You can use your bootstrap file to assign the EntityManager:\n\n" .
  83. '\Codeception\Module\Doctrine2::$em = $em');
  84. if ($this->config['cleanup'] && self::$em->getConnection()->isTransactionActive()) {
  85. self::$em->getConnection()->rollback();
  86. }
  87. $this->clean();
  88. }
  89. protected function clean()
  90. {
  91. $em = self::$em;
  92. $reflectedEm = new \ReflectionClass($em);
  93. if ($reflectedEm->hasProperty('repositories')) {
  94. $property = $reflectedEm->getProperty('repositories');
  95. $property->setAccessible(true);
  96. $property->setValue($em, array());
  97. }
  98. self::$em->clear();
  99. }
  100. /**
  101. * Performs $em->flush();
  102. */
  103. public function flushToDatabase()
  104. {
  105. self::$em->flush();
  106. }
  107. /**
  108. * Adds entity to repository and flushes. You can redefine it's properties with the second parameter.
  109. *
  110. * Example:
  111. *
  112. * ``` php
  113. * <?php
  114. * $I->persistEntity(new \Entity\User, array('name' => 'Miles'));
  115. * $I->persistEntity($user, array('name' => 'Miles'));
  116. * ```
  117. *
  118. * @param $obj
  119. * @param array $values
  120. */
  121. public function persistEntity($obj, $values = array()) {
  122. if ($values) {
  123. $reflectedObj = new \ReflectionClass($obj);
  124. foreach ($values as $key => $val) {
  125. $property = $reflectedObj->getProperty($key);
  126. $property->setAccessible(true);
  127. $property->setValue($obj, $val);
  128. }
  129. }
  130. self::$em->persist($obj);
  131. self::$em->flush();
  132. }
  133. /**
  134. * Mocks the repository.
  135. *
  136. * With this action you can redefine any method of any repository.
  137. * Please, note: this fake repositories will be accessible through entity manager till the end of test.
  138. *
  139. * Example:
  140. *
  141. * ``` php
  142. * <?php
  143. *
  144. * $I->haveFakeRepository('Entity\User', array('findByUsername' => function($username) { return null; }));
  145. *
  146. * ```
  147. *
  148. * This creates a stub class for Entity\User repository with redefined method findByUsername, which will always return the NULL value.
  149. *
  150. * @param $classname
  151. * @param array $methods
  152. */
  153. public function haveFakeRepository($classname, $methods = array())
  154. {
  155. $em = self::$em;
  156. $metadata = $em->getMetadataFactory()->getMetadataFor($classname);
  157. $customRepositoryClassName = $metadata->customRepositoryClassName;
  158. if (!$customRepositoryClassName) $customRepositoryClassName = '\Doctrine\ORM\EntityRepository';
  159. $mock = \Codeception\Util\Stub::make($customRepositoryClassName, array_merge(array('_entityName' => $metadata->name,
  160. '_em' => $em,
  161. '_class' => $metadata), $methods));
  162. $em->clear();
  163. $reflectedEm = new \ReflectionClass($em);
  164. if ($reflectedEm->hasProperty('repositories')) {
  165. $property = $reflectedEm->getProperty('repositories');
  166. $property->setAccessible(true);
  167. $property->setValue($em, array_merge($property->getValue($em), array($classname => $mock)));
  168. } else {
  169. $this->debugSection('Warning','Repository can\'t be mocked, the EventManager class doesn\'t have "repositories" property');
  170. }
  171. }
  172. /**
  173. * Persists record into repository.
  174. * This method crates an entity, and sets its properties directly (via reflection).
  175. * Setters of entity won't be executed, but you can create almost any entity and save it to database.
  176. * Returns id using `getId` of newly created entity.
  177. *
  178. * ```php
  179. * $I->haveInRepository('Entity\User', array('name' => 'davert'));
  180. * ```
  181. */
  182. public function haveInRepository($entity, array $data)
  183. {
  184. $reflectedEntity = new \ReflectionClass($entity);
  185. $entityObject = $reflectedEntity->newInstance();
  186. foreach ($reflectedEntity->getProperties() as $property) {
  187. /** @var $property \ReflectionProperty */
  188. if (!isset($data[$property->name])) {
  189. continue;
  190. }
  191. $property->setAccessible(true);
  192. $property->setValue($entityObject, $data[$property->name]);
  193. }
  194. self::$em->persist($entityObject);
  195. self::$em->flush();
  196. if (method_exists($entityObject, 'getId')) {
  197. $id = $entityObject->getId();
  198. $this->debug("$entity entity created with id:$id");
  199. return $id;
  200. }
  201. }
  202. /**
  203. * Flushes changes to database executes a query defined by array.
  204. * It builds query based on array of parameters.
  205. * You can use entity associations to build complex queries.
  206. *
  207. * Example:
  208. *
  209. * ``` php
  210. * <?php
  211. * $I->seeInRepository('User', array('name' => 'davert'));
  212. * $I->seeInRepository('User', array('name' => 'davert', 'Company' => array('name' => 'Codegyre')));
  213. * $I->seeInRepository('Client', array('User' => array('Company' => array('name' => 'Codegyre')));
  214. * ?>
  215. * ```
  216. *
  217. * Fails if record for given criteria can\'t be found,
  218. *
  219. * @param $entity
  220. * @param array $params
  221. */
  222. public function seeInRepository($entity, $params = array()) {
  223. $res = $this->proceedSeeInRepository($entity, $params);
  224. $this->assert($res);
  225. }
  226. /**
  227. * Flushes changes to database and performs ->findOneBy() call for current repository.
  228. *
  229. * @param $entity
  230. * @param array $params
  231. */
  232. public function dontSeeInRepository($entity, $params = array()) {
  233. $res = $this->proceedSeeInRepository($entity, $params);
  234. $this->assertNot($res);
  235. }
  236. protected function proceedSeeInRepository($entity, $params = array())
  237. {
  238. // we need to store to database...
  239. self::$em->flush();
  240. $data = self::$em->getClassMetadata($entity);
  241. $qb = self::$em->getRepository($entity)->createQueryBuilder('s');
  242. $this->buildAssociationQuery($qb,$entity, 's', $params);
  243. $this->debug($qb->getDQL());
  244. $res = $qb->getQuery()->getArrayResult();
  245. return array('True', (count($res) > 0), "$entity with " . json_encode($params));
  246. }
  247. /**
  248. * Selects field value from repository.
  249. * It builds query based on array of parameters.
  250. * You can use entity associations to build complex queries.
  251. *
  252. * Example:
  253. *
  254. * ``` php
  255. * <?php
  256. * $email = $I->grabFromRepository('User', 'email', array('name' => 'davert'));
  257. * ?>
  258. * ```
  259. *
  260. * @version 1.1
  261. * @param $entity
  262. * @param $field
  263. * @param array $params
  264. * @return array
  265. */
  266. public function grabFromRepository($entity, $field, $params = array())
  267. {
  268. // we need to store to database...
  269. self::$em->flush();
  270. $data = self::$em->getClassMetadata($entity);
  271. $qb = self::$em->getRepository($entity)->createQueryBuilder('s');
  272. $qb->select('s.'.$field);
  273. $this->buildAssociationQuery($qb,$entity, 's', $params);
  274. $this->debug($qb->getDQL());
  275. return $qb->getQuery()->getSingleScalarResult();
  276. }
  277. /**
  278. * It's Fuckin Recursive!
  279. *
  280. * @param $qb
  281. * @param $assoc
  282. * @param $alias
  283. * @param $params
  284. */
  285. protected function buildAssociationQuery($qb, $assoc, $alias, $params)
  286. {
  287. $data = self::$em->getClassMetadata($assoc);
  288. foreach ($params as $key => $val) {
  289. if (isset($data->associationMappings)) {
  290. if ($map = array_key_exists($key, $data->associationMappings)) {
  291. if (is_array($val)) {
  292. $qb->innerJoin("$alias.$key", $key);
  293. foreach ($val as $column => $v) {
  294. if (is_array($v)) {
  295. $this->buildAssociationQuery($qb, $map['targetEntity'], $column, $v);
  296. continue;
  297. }
  298. $paramname = $key.'__'.$column;
  299. $qb->andWhere("$key.$column = :$paramname");
  300. $qb->setParameter($paramname, $v);
  301. }
  302. continue;
  303. }
  304. }
  305. }
  306. if ($val === null) {
  307. $qb->andWhere("s.$key IS NULL");
  308. } else {
  309. $paramname = "s__$key";
  310. $qb->andWhere("s.$key = :$paramname");
  311. $qb->setParameter($paramname, $val);
  312. }
  313. }
  314. }
  315. }