瀏覽代碼

Merge branch 'stable/v0.9.6' of peyruis.bux.fr:/home/git/repositories/muzich into stable/v0.9.6

Bastien Sevajol 11 年之前
父節點
當前提交
c354b122bf
共有 100 個文件被更改,包括 17943 次插入54 次删除
  1. 2 2
      app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml
  2. 1 0
      app/Resources/FOSUserBundle/views/Resetting/email.txt.twig
  3. 1 1
      app/Resources/TwigBundle/views/Exception/error.html.twig
  4. 1 1
      app/Resources/TwigBundle/views/Exception/error403.html.twig
  5. 1 1
      app/Resources/TwigBundle/views/Exception/error404.html.twig
  6. 3 3
      app/Resources/translations/flash.fr.yml
  7. 34 15
      app/Resources/translations/text.fr.yml
  8. 5 5
      app/bootstrap.php.cache
  9. 1 1
      app/config/config_dev.yml
  10. 2 2
      app/config/config_prod.yml
  11. 12 1
      app/config/config_test.yml
  12. 4 0
      app/config/routing.yml
  13. 4 4
      composer.json
  14. 225 2
      composer.lock
  15. 0 2
      src/Muzich/AdminBundle/Controller/DashboardController.php
  16. 1 1
      src/Muzich/AdminBundle/Resources/views/Admin_userEdit/password.html.twig
  17. 0 0
      src/Muzich/AdminBundle/Resources/views/Dashboard/dashboard.html.twig
  18. 0 0
      src/Muzich/AdminBundle/Resources/views/Dashboard/dashboard.html.twig~HEAD
  19. 0 0
      src/Muzich/AdminBundle/Resources/views/Dashboard/welcome.html.twig
  20. 1 1
      src/Muzich/AdminBundle/Resources/views/Dashboard/welcome.html.twig~HEAD
  21. 1 1
      src/Muzich/AdminBundle/Resources/views/Moderate_commentList/list.html.twig
  22. 0 0
      src/Muzich/AdminBundle/Resources/views/base_admin_assetic_less.html.twig
  23. 0 0
      src/Muzich/AdminBundle/Resources/views/base_admin_assetic_less.html.twig~HEAD
  24. 1 1
      src/Muzich/CoreBundle/Command/CheckModerateCommand.php
  25. 10 0
      src/Muzich/CoreBundle/Controller/InfoController.php
  26. 17 1
      src/Muzich/CoreBundle/Factory/ElementFactory.php
  27. 64 0
      src/Muzich/CoreBundle/Factory/Elements/Vimeocom.php
  28. 4 0
      src/Muzich/CoreBundle/Managers/ElementManager.php
  29. 4 0
      src/Muzich/CoreBundle/Resources/config/routing.yml
  30. 11 3
      src/Muzich/CoreBundle/Resources/public/css/main.css
  31. 2 2
      src/Muzich/CoreBundle/Resources/public/js/player/YoutubePlayer.js
  32. 14 3
      src/Muzich/CoreBundle/Resources/views/Layout/head_js.html.twig
  33. 19 0
      src/Muzich/CoreBundle/Resources/views/layout.html.twig
  34. 14 0
      src/Muzich/CoreBundle/Tests/ElementFactory/ElementFactoryTest.php
  35. 1 0
      src/Muzich/IndexBundle/Resources/views/Presubscription/confirm.txt.twig
  36. 1 1
      src/Muzich/UserBundle/Controller/UserController.php
  37. 1 0
      src/Muzich/UserBundle/Resources/views/User/resetting.email.twig
  38. 7 0
      vendor/autoload.php
  39. 1 0
      vendor/bin/doctrine
  40. 1 0
      vendor/bin/doctrine.php
  41. 46 0
      vendor/composer/autoload_real.php
  42. 2371 0
      vendor/composer/installed.json
  43. 55 0
      vendor/doctrine/common/lib/Doctrine/Common/Version.php
  44. 26 0
      vendor/doctrine/dbal/composer.json
  45. 927 0
      vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php
  46. 183 0
      vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php
  47. 55 0
      vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php
  48. 245 0
      vendor/doctrine/dbal/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php
  49. 252 0
      vendor/doctrine/dbal/tests/Doctrine/Tests/DBAL/SQLParserUtilsTest.php
  50. 8 0
      vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/.travis.yml
  51. 32 0
      vendor/doctrine/orm/composer.json
  52. 59 0
      vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php
  53. 537 0
      vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
  54. 3050 0
      vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php
  55. 55 0
      vendor/doctrine/orm/lib/Doctrine/ORM/Version.php
  56. 464 0
      vendor/doctrine/orm/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php
  57. 1098 0
      vendor/doctrine/orm/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
  58. 142 0
      vendor/gedmo/doctrine-extensions/README.md
  59. 219 0
      vendor/gedmo/doctrine-extensions/doc/references.md
  60. 1265 0
      vendor/gedmo/doctrine-extensions/doc/tree.md
  61. 95 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php
  62. 25 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php
  63. 21 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php
  64. 21 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceOne.php
  65. 31 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php
  66. 22 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php
  67. 215 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php
  68. 99 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php
  69. 99 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php
  70. 98 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php
  71. 50 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php
  72. 158 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php
  73. 50 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php
  74. 486 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php
  75. 83 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php
  76. 99 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php
  77. 99 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php
  78. 131 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php
  79. 745 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php
  80. 250 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php
  81. 197 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php
  82. 230 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php
  83. 514 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/AbstractMaterializedPath.php
  84. 28 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php
  85. 28 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php
  86. 25 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php
  87. 709 0
      vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php
  88. 1 0
      vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping.xsd
  89. 58 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/References/Fixture/ODM/MongoDB/Product.php
  90. 105 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/References/Fixture/ORM/StockItem.php
  91. 123 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/References/ReferencesListenerTest.php
  92. 331 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/Tool/BaseTestCaseOM.php
  93. 108 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/Tree/Fixture/MPFeaturesCategory.php
  94. 109 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/Tree/MaterializedPathORMFeaturesTest.php
  95. 27 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumericTest.php
  96. 698 0
      vendor/gedmo/doctrine-extensions/tests/Gedmo/Uploadable/UploadableEntityTest.php
  97. 62 0
      vendor/gedmo/doctrine-extensions/tests/bootstrap.php
  98. 66 0
      vendor/gedmo/doctrine-extensions/tests/phpunit.xml.dist
  99. 87 0
      vendor/jdorn/sql-formatter/README.md
  100. 0 0
      vendor/jdorn/sql-formatter/examples/SqlFormatterExample.png

+ 2 - 2
app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml 查看文件

@@ -37,7 +37,7 @@ change_password:
37 37
 
38 38
 # Registration
39 39
 registration:
40
-    check_email: Un e-mail a été envoyé. Veuillez suivre les instructions indiquées.
40
+    check_email: Un e-mail a été envoyé. Veuillez suivre les instructions indiquées (vérifiez aussi dans vos SPAM).
41 41
     confirmed: Félicitations %username%, votre compte est maintenant activé.
42 42
     back: Retour à la page d'origine.
43 43
     submit: Enregistrer
@@ -55,7 +55,7 @@ registration:
55 55
 # Password resetting
56 56
 resetting:
57 57
     password_already_requested: Un nouveau mot de passe a déjà été demandé pour cet utilisateur dans les dernières 24 heures.
58
-    check_email: Un e-mail a été envoyé. Veuillez suivre les instructions indiquées.
58
+    check_email: Un e-mail a été envoyé. Veuillez suivre les instructions indiquées (vérifiez aussi dans vos SPAM).
59 59
     request:
60 60
         invalid_username: Le nom d'utilisateur ou l'adresse e-mail "%username%" n'existe pas.
61 61
         username: "Nom d'utilisateur ou adresse e-mail:"

+ 1 - 0
app/Resources/FOSUserBundle/views/Resetting/email.txt.twig 查看文件

@@ -6,5 +6,6 @@
6 6
       '%url%'      : confirmationUrl,
7 7
       '%username%' : user.username
8 8
     }, 'text') }}
9
+    {{ 'mail.general.footer'|trans({}, 'text') }}
9 10
   {% endautoescape %}
10 11
 {% endblock %}

+ 1 - 1
app/Resources/TwigBundle/views/Exception/error.html.twig 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "JSPCoreBundle::layout_error.html.twig" %}
1
+{% extends 'MuzichCoreBundle::layout.html.twig' %}
2 2
 
3 3
 {% block title %}{{ 'error_page.error.title'|trans({}, 'navigationui') }}{% endblock %}
4 4
 {% block mainbox_classes %}mainbox_padding{% endblock %}

+ 1 - 1
app/Resources/TwigBundle/views/Exception/error403.html.twig 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "JSPCoreBundle::layout_error.html.twig" %}
1
+{% extends 'MuzichCoreBundle::layout.html.twig' %}
2 2
 
3 3
 {% block title %}{{ 'error_page.forbidden.title'|trans({}, 'navigationui') }}{% endblock %}
4 4
 {% block mainbox_classes %}mainbox_padding{% endblock %}

+ 1 - 1
app/Resources/TwigBundle/views/Exception/error404.html.twig 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "JSPCoreBundle::layout_error.html.twig" %}
1
+{% extends 'MuzichCoreBundle::layout.html.twig' %}
2 2
 
3 3
 {% block title %}{{ 'error_page.notfound.title'|trans({}, 'navigationui') }}{% endblock %}
4 4
 {% block mainbox_classes %}mainbox_padding{% endblock %}

+ 3 - 3
app/Resources/translations/flash.fr.yml 查看文件

@@ -22,17 +22,17 @@ element:
22 22
     error:        Une erreur est survenus lors de l'ajout.
23 23
   remove:
24 24
     success:      Element supprimé avec succés.
25
-    error:        Une errur est survenue durant la suppression de l'élément.
25
+    error:        Une erreur est survenue durant la suppression de l'élément.
26 26
     
27 27
 user:
28 28
   changeemail:
29
-    mail_send:     Un email a été envoyé a l'adresse communiqué pour confirmation.
29
+    mail_send:     Un email a été envoyé a l'adresse communiqué pour confirmation (vérifiez aussi dans vos SPAM).
30 30
     token_invalid: Le lien pour mettre a jour votre email est périmé, veuillez relancer la procédure.
31 31
     success:       Votre email a correctement été mis a jour.
32 32
     wait:          Vous devez attendre avant de pouvoir faire une nouvelle demande de changement d'email.
33 33
   session_expired: Vous avez été déconnecté, veuillez vous ré-identifier
34 34
 
35 35
 presubscription:
36
-  success:         Un email a été envoyé a votre adresse email pour confirmation
36
+  success:         Un email a été envoyé a votre adresse email pour confirmation (vérifiez aussi dans vos SPAM)
37 37
   error:           Une erreur est survenue, veuillez réessayer
38 38
   confirmed:       Votre email a bien été pris en compte, merci !

+ 34 - 15
app/Resources/translations/text.fr.yml 查看文件

@@ -19,6 +19,20 @@ about:
19 19
                   sur ce même réseau.
20 20
 
21 21
 mail:
22
+  general:
23
+    footer:       >
24
+                  
25
+                  
26
+                  --------------------------------
27
+                  
28
+                  Pour nous contacter directement : contact@muzi.ch
29
+                  
30
+                  Pour supprimer tout contact futur envoyez simplement un mail à : unsubscribe@muzi.ch
31
+                  
32
+                  
33
+                  Muzi.ch - Découvrez et partagez vos découvertes musicales !
34
+                  
35
+                  12b avenue de la roche, 04310 Peyruis, FRANCE.
22 36
   resetting_password:
23 37
     subject:      Muzi.ch: Réinitialisation du mot de passe
24 38
     content:      >
@@ -30,9 +44,10 @@ mail:
30 44
                   Si au contraire vous souhaitez changer le mot de passe de votre accès a Muzi.ch
31 45
                   rendez-vous à cette adresse: %url%
32 46
                   
47
+                  
33 48
                   Cordialement,
34 49
                   
35
-                  l'équipe de Muzi.ch
50
+                  L'équipe de Muzi.ch
36 51
   change_email:
37 52
     subject:      Muzi.ch: Changement de courriel
38 53
     content:      >
@@ -46,21 +61,25 @@ mail:
46 61
                   
47 62
                   Cordialement,
48 63
                   
49
-                  l'équipe de Muzi.ch
64
+                  L'équipe de Muzi.ch
50 65
   presubscription:
51
-    subject:         Muzi.ch: Inscription pour l'ouverture
52
-    text:            >
53
-                      Bonjour,
54
-                      
55
-                      Une demande d'inscription de cet email a Muzi.ch pour son ouverture oficielle a été demandé.
56
-                      Si vous n'êtes pas a l'origine de cette demande, ne tenez pas compte de cet email.
57
-                      
58
-                      Si au contraire vous souhaitez être avertis de l'ouverture officielle de
59
-                      Muzi.ch, rendez vous à cette adresse: %url%
60
-                      
61
-                      Cordialement,
62
-                      
63
-                      l'équipe de Muzi.ch
66
+    subject:        Muzi.ch: Confirmation de demande d'incription 
67
+    text:           >
68
+                    Ceci est un émail automatique, merci d'utiliser contact@muzi.ch pour nous répondre.
69
+                    
70
+                    
71
+                    Une demande d'inscription à Muzi.ch a été demandée.
72
+                    
73
+                    Si vous n'êtes pas à l'origine de cette demande, ne tenez pas compte de cet email.
74
+                    
75
+                    Sinon, pour confirmer votre demande d'inscription il vous suffit de suivre ce lien : %url%
76
+                    
77
+                    Nous vous remercions pour votre intérêt.
78
+                    
79
+                    
80
+                    Cordialement,
81
+                    
82
+                    L'équipe de Muzi.ch
64 83
 
65 84
 help:
66 85
   element_add_url:

+ 5 - 5
app/bootstrap.php.cache 查看文件

@@ -511,11 +511,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
511 511
     protected $classes;
512 512
     protected $errorReportingLevel;
513 513
 
514
-    const VERSION         = '2.1.9';
515
-    const VERSION_ID      = '20109';
514
+    const VERSION         = '2.1.8';
515
+    const VERSION_ID      = '20108';
516 516
     const MAJOR_VERSION   = '2';
517 517
     const MINOR_VERSION   = '1';
518
-    const RELEASE_VERSION = '9';
518
+    const RELEASE_VERSION = '8';
519 519
     const EXTRA_VERSION   = '';
520 520
 
521 521
     
@@ -1643,12 +1643,12 @@ class HttpKernel extends BaseHttpKernel
1643 1643
         $request = $this->container->get('request');
1644 1644
 
1645 1645
                 if (0 === strpos($controller, 'http://') || 0 === strpos($controller, 'https://')) {
1646
-            $subRequest = $request::create($controller, 'get', array(), $request->cookies->all(), array(), $request->server->all());
1646
+            $subRequest = Request::create($controller, 'get', array(), $request->cookies->all(), array(), $request->server->all());
1647 1647
             if ($session = $request->getSession()) {
1648 1648
                 $subRequest->setSession($session);
1649 1649
             }
1650 1650
         } elseif (0 === strpos($controller, '/')) {
1651
-            $subRequest = $request::create($request->getUriForPath($controller), 'get', array(), $request->cookies->all(), array(), $request->server->all());
1651
+            $subRequest = Request::create($request->getUriForPath($controller), 'get', array(), $request->cookies->all(), array(), $request->server->all());
1652 1652
             if ($session = $request->getSession()) {
1653 1653
                 $subRequest->setSession($session);
1654 1654
             }

+ 1 - 1
app/config/config_dev.yml 查看文件

@@ -11,7 +11,7 @@ web_profiler:
11 11
     
12 12
 swiftmailer:
13 13
     transport:  smtp
14
-    username:   noreply@muzi.ch
14
+    username:   contact@muzi.ch
15 15
     password:   ***REMOVED***
16 16
     host:       mx.muzi.ch
17 17
     port:       587

+ 2 - 2
app/config/config_prod.yml 查看文件

@@ -26,9 +26,9 @@ monolog:
26 26
             handler: swift
27 27
         swift:
28 28
             type:       swift_mailer
29
-            from_email: error@smuzi.ch
29
+            from_email: contact@muzi.ch
30 30
             to_email:   sevajol.bastien@gmail.com
31
-            subject:    An Error Occurred on muzi.ch!
31
+            subject:    An Error Occurred (muzi.ch)!
32 32
             level:      debug
33 33
 
34 34
 parameters:

+ 12 - 1
app/config/config_test.yml 查看文件

@@ -14,5 +14,16 @@ web_profiler:
14 14
 swiftmailer:
15 15
     disable_delivery: true
16 16
 
17
+# Doctrine Configuration
18
+doctrine:
19
+    dbal:
20
+        driver:   %database_driver%
21
+        host:     %database_host%
22
+        port:     %database_port%
23
+        dbname:   muzich_test
24
+        user:     %database_user%
25
+        password: %database_password%
26
+        charset:  utf8
27
+
17 28
 parameters:
18
-    env: test
29
+    env: test

+ 4 - 0
app/config/routing.yml 查看文件

@@ -48,6 +48,10 @@ MuzichCommentBundle:
48 48
   resource: "@MuzichCommentBundle/Resources/config/routing.yml"
49 49
   prefix:   /{_locale}/
50 50
   
51
+AdmingeneratorDashboard_welcome:
52
+    pattern: /admin/dashboard
53
+    defaults: { _controller: MuzichAdminBundle:Dashboard:welcome }
54
+
51 55
 shtumi_useful:
52 56
     resource: '@ShtumiUsefulBundle/Resources/config/routing.xml'
53 57
     prefix:   /system/search/

+ 4 - 4
composer.json 查看文件

@@ -7,8 +7,8 @@
7 7
     },
8 8
     "require": {
9 9
         "php": ">=5.3.3",
10
-        "symfony/symfony": "2.1.*",
11
-        "doctrine/orm": ">=2.2.3,<2.4-dev",
10
+        "symfony/symfony": "2.1.8",
11
+        "doctrine/orm": ">=2.2.3,<2.5-dev",
12 12
         "doctrine/doctrine-bundle": "1.1.*",
13 13
         "twig/extensions": "1.0.*@dev",
14 14
         "symfony/assetic-bundle": "2.1.*",
@@ -22,9 +22,9 @@
22 22
         "kriswallsmith/assetic": "1.1.*@dev",
23 23
         
24 24
         "friendsofsymfony/user-bundle": "v1.3.1",
25
-        "doctrine/data-fixtures" : "dev-master",
25
+        "doctrine/data-fixtures" : "dev-master", 
26 26
         "doctrine/doctrine-fixtures-bundle": "dev-master",
27
-        "stof/doctrine-extensions-bundle": "dev-master",
27
+        "stof/doctrine-extensions-bundle": "dev-master",  
28 28
         "gregwar/image-bundle": "dev-master",
29 29
         "cedriclombardot/admingenerator-generator-bundle": "1.0.*@dev",
30 30
         "cedriclombardot/twig-generator": "dev-master",

+ 225 - 2
composer.lock 查看文件

@@ -1,5 +1,9 @@
1 1
 {
2
+<<<<<<< HEAD
2 3
     "hash": "8fa2363ec51be0f17ab9adab0dcf22d0",
4
+=======
5
+    "hash": "8c20901e9c7b4d5355afa461450af0ae",
6
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
3 7
     "packages": [
4 8
         {
5 9
             "name": "cedriclombardot/admingenerator-generator-bundle",
@@ -8,12 +12,21 @@
8 12
             "source": {
9 13
                 "type": "git",
10 14
                 "url": "https://github.com/symfony2admingenerator/AdmingeneratorGeneratorBundle.git",
15
+<<<<<<< HEAD
11 16
                 "reference": "dbcbd469aec90551e48730075f310d9bf6d92cb6"
12 17
             },
13 18
             "dist": {
14 19
                 "type": "zip",
15 20
                 "url": "https://api.github.com/repos/symfony2admingenerator/AdmingeneratorGeneratorBundle/zipball/dbcbd469aec90551e48730075f310d9bf6d92cb6",
16 21
                 "reference": "dbcbd469aec90551e48730075f310d9bf6d92cb6",
22
+=======
23
+                "reference": "b46f56fbdb24c74bf296cef1e0e8f8fbbb504cb1"
24
+            },
25
+            "dist": {
26
+                "type": "zip",
27
+                "url": "https://api.github.com/repos/symfony2admingenerator/AdmingeneratorGeneratorBundle/zipball/b46f56fbdb24c74bf296cef1e0e8f8fbbb504cb1",
28
+                "reference": "b46f56fbdb24c74bf296cef1e0e8f8fbbb504cb1",
29
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
17 30
                 "shasum": ""
18 31
             },
19 32
             "require": {
@@ -32,7 +45,11 @@
32 45
             "suggest": {
33 46
                 "cedriclombardot/admingenerator-user-bundle": "Help you to overwrite the base layout of FOSUserBundle or others giving a parameter key of the container"
34 47
             },
48
+<<<<<<< HEAD
35 49
             "time": "2013-03-26 07:49:40",
50
+=======
51
+            "time": "2013-03-10 18:15:34",
52
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
36 53
             "type": "symfony-bundle",
37 54
             "extra": {
38 55
                 "branch-alias": {
@@ -97,7 +114,7 @@
97 114
             ],
98 115
             "authors": [
99 116
                 {
100
-                    "name": "Cédric Lombardot",
117
+                    "name": "Cedric LOMBARDOT",
101 118
                     "email": "cedric.lombardot@gmail.com"
102 119
                 },
103 120
                 {
@@ -237,6 +254,7 @@
237 254
         },
238 255
         {
239 256
             "name": "doctrine/dbal",
257
+<<<<<<< HEAD
240 258
             "version": "2.3.3",
241 259
             "source": {
242 260
                 "type": "git",
@@ -247,13 +265,29 @@
247 265
                 "type": "zip",
248 266
                 "url": "https://api.github.com/repos/doctrine/dbal/zipball/2.3.3",
249 267
                 "reference": "2.3.3",
268
+=======
269
+            "version": "2.3.2",
270
+            "source": {
271
+                "type": "git",
272
+                "url": "https://github.com/doctrine/dbal",
273
+                "reference": "2.3.2"
274
+            },
275
+            "dist": {
276
+                "type": "zip",
277
+                "url": "https://github.com/doctrine/dbal/archive/2.3.2.zip",
278
+                "reference": "2.3.2",
279
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
250 280
                 "shasum": ""
251 281
             },
252 282
             "require": {
253 283
                 "doctrine/common": ">=2.3.0,<2.5-dev",
254 284
                 "php": ">=5.3.2"
255 285
             },
286
+<<<<<<< HEAD
256 287
             "time": "2013-03-24 19:16:29",
288
+=======
289
+            "time": "2013-01-07 20:03:43",
290
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
257 291
             "type": "library",
258 292
             "extra": {
259 293
                 "branch-alias": {
@@ -427,6 +461,7 @@
427 461
         },
428 462
         {
429 463
             "name": "doctrine/orm",
464
+<<<<<<< HEAD
430 465
             "version": "2.3.3",
431 466
             "source": {
432 467
                 "type": "git",
@@ -437,6 +472,18 @@
437 472
                 "type": "zip",
438 473
                 "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/2.3.3",
439 474
                 "reference": "2.3.3",
475
+=======
476
+            "version": "2.3.2",
477
+            "source": {
478
+                "type": "git",
479
+                "url": "git://github.com/doctrine/doctrine2.git",
480
+                "reference": "2.3.2"
481
+            },
482
+            "dist": {
483
+                "type": "zip",
484
+                "url": "https://github.com/doctrine/doctrine2/archive/2.3.2.zip",
485
+                "reference": "2.3.2",
486
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
440 487
                 "shasum": ""
441 488
             },
442 489
             "require": {
@@ -448,7 +495,11 @@
448 495
             "suggest": {
449 496
                 "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
450 497
             },
498
+<<<<<<< HEAD
451 499
             "time": "2013-03-24 20:43:58",
500
+=======
501
+            "time": "2013-01-07 20:05:04",
502
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
452 503
             "bin": [
453 504
                 "bin/doctrine",
454 505
                 "bin/doctrine.php"
@@ -764,7 +815,7 @@
764 815
                 {
765 816
                     "name": "Johannes M. Schmitt",
766 817
                     "email": "schmittjoh@gmail.com",
767
-                    "homepage": "https://github.com/schmittjoh",
818
+                    "homepage": "http://jmsyst.com",
768 819
                     "role": "Developer of wrapped JMSSerializerBundle"
769 820
                 }
770 821
             ],
@@ -1104,12 +1155,21 @@
1104 1155
             "source": {
1105 1156
                 "type": "git",
1106 1157
                 "url": "https://github.com/kriswallsmith/assetic.git",
1158
+<<<<<<< HEAD
1107 1159
                 "reference": "50a24301b40cd7df29a7da4efd5573a9ef4622f8"
1108 1160
             },
1109 1161
             "dist": {
1110 1162
                 "type": "zip",
1111 1163
                 "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/50a24301b40cd7df29a7da4efd5573a9ef4622f8",
1112 1164
                 "reference": "50a24301b40cd7df29a7da4efd5573a9ef4622f8",
1165
+=======
1166
+                "reference": "fcee205a5a3a859c009d976ba3acfe67958e912c"
1167
+            },
1168
+            "dist": {
1169
+                "type": "zip",
1170
+                "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/fcee205a5a3a859c009d976ba3acfe67958e912c",
1171
+                "reference": "fcee205a5a3a859c009d976ba3acfe67958e912c",
1172
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1113 1173
                 "shasum": ""
1114 1174
             },
1115 1175
             "require": {
@@ -1135,7 +1195,11 @@
1135 1195
                 "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris",
1136 1196
                 "twig/twig": "Assetic provides the integration with the Twig templating engine"
1137 1197
             },
1198
+<<<<<<< HEAD
1138 1199
             "time": "2013-03-25 20:14:55",
1200
+=======
1201
+            "time": "2013-03-05 17:56:54",
1202
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1139 1203
             "type": "library",
1140 1204
             "extra": {
1141 1205
                 "branch-alias": {
@@ -1231,12 +1295,21 @@
1231 1295
             "source": {
1232 1296
                 "type": "git",
1233 1297
                 "url": "https://github.com/whiteoctober/Pagerfanta.git",
1298
+<<<<<<< HEAD
1234 1299
                 "reference": "ae0ff215b3be2c3bd0b131a8b8daa9b15568c6db"
1235 1300
             },
1236 1301
             "dist": {
1237 1302
                 "type": "zip",
1238 1303
                 "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/ae0ff215b3be2c3bd0b131a8b8daa9b15568c6db",
1239 1304
                 "reference": "ae0ff215b3be2c3bd0b131a8b8daa9b15568c6db",
1305
+=======
1306
+                "reference": "878aee5e274ca75fbdf74262efd05d1207c31669"
1307
+            },
1308
+            "dist": {
1309
+                "type": "zip",
1310
+                "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/878aee5e274ca75fbdf74262efd05d1207c31669",
1311
+                "reference": "878aee5e274ca75fbdf74262efd05d1207c31669",
1312
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1240 1313
                 "shasum": ""
1241 1314
             },
1242 1315
             "require": {
@@ -1248,6 +1321,7 @@
1248 1321
                 "mandango/mandango": "*",
1249 1322
                 "propel/propel1": ">=1.6,<2.0",
1250 1323
                 "solarium/solarium": "3.*"
1324
+<<<<<<< HEAD
1251 1325
             },
1252 1326
             "suggest": {
1253 1327
                 "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.",
@@ -1257,6 +1331,17 @@
1257 1331
                 "solarium/solarium": "To use the SolariumAdapter."
1258 1332
             },
1259 1333
             "time": "2013-03-12 22:52:44",
1334
+=======
1335
+            },
1336
+            "suggest": {
1337
+                "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.",
1338
+                "doctrine/orm": "To use the DoctrineORMAdapter.",
1339
+                "mandango/mandango": "To use the MandangoAdapter.",
1340
+                "propel/propel1": "To use the PropelAdapter",
1341
+                "solarium/solarium": "To use the SolariumAdapter."
1342
+            },
1343
+            "time": "2013-02-19 13:51:25",
1344
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1260 1345
             "type": "library",
1261 1346
             "extra": {
1262 1347
                 "branch-alias": {
@@ -1287,17 +1372,30 @@
1287 1372
         },
1288 1373
         {
1289 1374
             "name": "sensio/distribution-bundle",
1375
+<<<<<<< HEAD
1290 1376
             "version": "v2.1.9",
1377
+=======
1378
+            "version": "v2.1.8",
1379
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1291 1380
             "target-dir": "Sensio/Bundle/DistributionBundle",
1292 1381
             "source": {
1293 1382
                 "type": "git",
1294 1383
                 "url": "https://github.com/sensio/SensioDistributionBundle.git",
1384
+<<<<<<< HEAD
1295 1385
                 "reference": "v2.1.9"
1296 1386
             },
1297 1387
             "dist": {
1298 1388
                 "type": "zip",
1299 1389
                 "url": "https://api.github.com/repos/sensio/SensioDistributionBundle/zipball/v2.1.9",
1300 1390
                 "reference": "v2.1.9",
1391
+=======
1392
+                "reference": "v2.1.8"
1393
+            },
1394
+            "dist": {
1395
+                "type": "zip",
1396
+                "url": "https://api.github.com/repos/sensio/SensioDistributionBundle/zipball/v2.1.8",
1397
+                "reference": "v2.1.8",
1398
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1301 1399
                 "shasum": ""
1302 1400
             },
1303 1401
             "require": {
@@ -1333,17 +1431,30 @@
1333 1431
         },
1334 1432
         {
1335 1433
             "name": "sensio/framework-extra-bundle",
1434
+<<<<<<< HEAD
1336 1435
             "version": "v2.1.9",
1436
+=======
1437
+            "version": "v2.1.8",
1438
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1337 1439
             "target-dir": "Sensio/Bundle/FrameworkExtraBundle",
1338 1440
             "source": {
1339 1441
                 "type": "git",
1340 1442
                 "url": "https://github.com/sensio/SensioFrameworkExtraBundle.git",
1443
+<<<<<<< HEAD
1341 1444
                 "reference": "v2.1.9"
1342 1445
             },
1343 1446
             "dist": {
1344 1447
                 "type": "zip",
1345 1448
                 "url": "https://api.github.com/repos/sensio/SensioFrameworkExtraBundle/zipball/v2.1.9",
1346 1449
                 "reference": "v2.1.9",
1450
+=======
1451
+                "reference": "v2.1.8"
1452
+            },
1453
+            "dist": {
1454
+                "type": "zip",
1455
+                "url": "https://api.github.com/repos/sensio/SensioFrameworkExtraBundle/zipball/v2.1.8",
1456
+                "reference": "v2.1.8",
1457
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1347 1458
                 "shasum": ""
1348 1459
             },
1349 1460
             "require": {
@@ -1380,17 +1491,30 @@
1380 1491
         },
1381 1492
         {
1382 1493
             "name": "sensio/generator-bundle",
1494
+<<<<<<< HEAD
1383 1495
             "version": "v2.1.9",
1496
+=======
1497
+            "version": "v2.1.8",
1498
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1384 1499
             "target-dir": "Sensio/Bundle/GeneratorBundle",
1385 1500
             "source": {
1386 1501
                 "type": "git",
1387 1502
                 "url": "https://github.com/sensio/SensioGeneratorBundle.git",
1503
+<<<<<<< HEAD
1388 1504
                 "reference": "v2.1.9"
1389 1505
             },
1390 1506
             "dist": {
1391 1507
                 "type": "zip",
1392 1508
                 "url": "https://api.github.com/repos/sensio/SensioGeneratorBundle/zipball/v2.1.9",
1393 1509
                 "reference": "v2.1.9",
1510
+=======
1511
+                "reference": "v2.1.8"
1512
+            },
1513
+            "dist": {
1514
+                "type": "zip",
1515
+                "url": "https://api.github.com/repos/sensio/SensioGeneratorBundle/zipball/v2.1.8",
1516
+                "reference": "v2.1.8",
1517
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1394 1518
                 "shasum": ""
1395 1519
             },
1396 1520
             "require": {
@@ -1433,12 +1557,21 @@
1433 1557
             "source": {
1434 1558
                 "type": "git",
1435 1559
                 "url": "https://github.com/shtumi/ShtumiUsefulBundle.git",
1560
+<<<<<<< HEAD
1436 1561
                 "reference": "368fb9003ff46c2204ebe5336d11f1e90853034a"
1437 1562
             },
1438 1563
             "dist": {
1439 1564
                 "type": "zip",
1440 1565
                 "url": "https://api.github.com/repos/shtumi/ShtumiUsefulBundle/zipball/368fb9003ff46c2204ebe5336d11f1e90853034a",
1441 1566
                 "reference": "368fb9003ff46c2204ebe5336d11f1e90853034a",
1567
+=======
1568
+                "reference": "16b1d259f352eb5f31037352904e525b1852791a"
1569
+            },
1570
+            "dist": {
1571
+                "type": "zip",
1572
+                "url": "https://api.github.com/repos/shtumi/ShtumiUsefulBundle/zipball/16b1d259f352eb5f31037352904e525b1852791a",
1573
+                "reference": "16b1d259f352eb5f31037352904e525b1852791a",
1574
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1442 1575
                 "shasum": ""
1443 1576
             },
1444 1577
             "require": {
@@ -1448,7 +1581,11 @@
1448 1581
                 "sonata-project/admin-bundle": "master-dev",
1449 1582
                 "sonata-project/doctrine-orm-admin-bundle": "master-dev"
1450 1583
             },
1584
+<<<<<<< HEAD
1451 1585
             "time": "2013-03-25 12:10:45",
1586
+=======
1587
+            "time": "2013-03-06 17:06:49",
1588
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1452 1589
             "type": "symfony-bundle",
1453 1590
             "autoload": {
1454 1591
                 "psr-0": {
@@ -1480,12 +1617,21 @@
1480 1617
             "source": {
1481 1618
                 "type": "git",
1482 1619
                 "url": "https://github.com/sonata-project/SonataAdminBundle.git",
1620
+<<<<<<< HEAD
1483 1621
                 "reference": "25ce399e05dbf8685111003780aecbcc2ef968cc"
1484 1622
             },
1485 1623
             "dist": {
1486 1624
                 "type": "zip",
1487 1625
                 "url": "https://api.github.com/repos/sonata-project/SonataAdminBundle/zipball/25ce399e05dbf8685111003780aecbcc2ef968cc",
1488 1626
                 "reference": "25ce399e05dbf8685111003780aecbcc2ef968cc",
1627
+=======
1628
+                "reference": "8b4266b2c8b6e66101a1a585f9e7a398213a7c78"
1629
+            },
1630
+            "dist": {
1631
+                "type": "zip",
1632
+                "url": "https://api.github.com/repos/sonata-project/SonataAdminBundle/zipball/8b4266b2c8b6e66101a1a585f9e7a398213a7c78",
1633
+                "reference": "8b4266b2c8b6e66101a1a585f9e7a398213a7c78",
1634
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1489 1635
                 "shasum": ""
1490 1636
             },
1491 1637
             "require": {
@@ -1512,7 +1658,11 @@
1512 1658
                 "sonata-project/doctrine-orm-admin-bundle": "2.1.*@dev",
1513 1659
                 "sonata-project/intl-bundle": "2.1.*"
1514 1660
             },
1661
+<<<<<<< HEAD
1515 1662
             "time": "2013-03-27 16:46:49",
1663
+=======
1664
+            "time": "2013-03-10 13:51:33",
1665
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1516 1666
             "type": "symfony-bundle",
1517 1667
             "extra": {
1518 1668
                 "branch-alias": {
@@ -1671,6 +1821,7 @@
1671 1821
         },
1672 1822
         {
1673 1823
             "name": "sonata-project/exporter",
1824
+<<<<<<< HEAD
1674 1825
             "version": "1.2.1",
1675 1826
             "source": {
1676 1827
                 "type": "git",
@@ -1681,6 +1832,18 @@
1681 1832
                 "type": "zip",
1682 1833
                 "url": "https://api.github.com/repos/sonata-project/exporter/zipball/1.2.1",
1683 1834
                 "reference": "1.2.1",
1835
+=======
1836
+            "version": "1.2.0",
1837
+            "source": {
1838
+                "type": "git",
1839
+                "url": "https://github.com/sonata-project/exporter.git",
1840
+                "reference": "1.2.0"
1841
+            },
1842
+            "dist": {
1843
+                "type": "zip",
1844
+                "url": "https://api.github.com/repos/sonata-project/exporter/zipball/1.2.0",
1845
+                "reference": "1.2.0",
1846
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1684 1847
                 "shasum": ""
1685 1848
             },
1686 1849
             "require": {
@@ -1694,7 +1857,11 @@
1694 1857
                 "ext-curl": "*",
1695 1858
                 "symfony/routing": "*"
1696 1859
             },
1860
+<<<<<<< HEAD
1697 1861
             "time": "2013-03-04 14:20:46",
1862
+=======
1863
+            "time": "2013-02-22 11:58:00",
1864
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1698 1865
             "type": "library",
1699 1866
             "autoload": {
1700 1867
                 "psr-0": {
@@ -1946,17 +2113,30 @@
1946 2113
         },
1947 2114
         {
1948 2115
             "name": "symfony/monolog-bundle",
2116
+<<<<<<< HEAD
1949 2117
             "version": "v2.1.9",
2118
+=======
2119
+            "version": "v2.1.8",
2120
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1950 2121
             "target-dir": "Symfony/Bundle/MonologBundle",
1951 2122
             "source": {
1952 2123
                 "type": "git",
1953 2124
                 "url": "https://github.com/symfony/MonologBundle.git",
2125
+<<<<<<< HEAD
1954 2126
                 "reference": "v2.1.9"
1955 2127
             },
1956 2128
             "dist": {
1957 2129
                 "type": "zip",
1958 2130
                 "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/v2.1.9",
1959 2131
                 "reference": "v2.1.9",
2132
+=======
2133
+                "reference": "v2.1.8"
2134
+            },
2135
+            "dist": {
2136
+                "type": "zip",
2137
+                "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/v2.1.8",
2138
+                "reference": "v2.1.8",
2139
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
1960 2140
                 "shasum": ""
1961 2141
             },
1962 2142
             "require": {
@@ -2005,17 +2185,30 @@
2005 2185
         },
2006 2186
         {
2007 2187
             "name": "symfony/swiftmailer-bundle",
2188
+<<<<<<< HEAD
2008 2189
             "version": "v2.1.9",
2190
+=======
2191
+            "version": "v2.1.8",
2192
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2009 2193
             "target-dir": "Symfony/Bundle/SwiftmailerBundle",
2010 2194
             "source": {
2011 2195
                 "type": "git",
2012 2196
                 "url": "https://github.com/symfony/SwiftmailerBundle.git",
2197
+<<<<<<< HEAD
2013 2198
                 "reference": "v2.1.9"
2014 2199
             },
2015 2200
             "dist": {
2016 2201
                 "type": "zip",
2017 2202
                 "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/v2.1.9",
2018 2203
                 "reference": "v2.1.9",
2204
+=======
2205
+                "reference": "v2.1.8"
2206
+            },
2207
+            "dist": {
2208
+                "type": "zip",
2209
+                "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/v2.1.8",
2210
+                "reference": "v2.1.8",
2211
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2019 2212
                 "shasum": ""
2020 2213
             },
2021 2214
             "require": {
@@ -2060,6 +2253,7 @@
2060 2253
         },
2061 2254
         {
2062 2255
             "name": "symfony/symfony",
2256
+<<<<<<< HEAD
2063 2257
             "version": "v2.1.9",
2064 2258
             "source": {
2065 2259
                 "type": "git",
@@ -2070,6 +2264,18 @@
2070 2264
                 "type": "zip",
2071 2265
                 "url": "https://api.github.com/repos/symfony/symfony/zipball/v2.1.9",
2072 2266
                 "reference": "v2.1.9",
2267
+=======
2268
+            "version": "v2.1.8",
2269
+            "source": {
2270
+                "type": "git",
2271
+                "url": "https://github.com/symfony/symfony.git",
2272
+                "reference": "v2.1.8"
2273
+            },
2274
+            "dist": {
2275
+                "type": "zip",
2276
+                "url": "https://api.github.com/repos/symfony/symfony/zipball/v2.1.8",
2277
+                "reference": "v2.1.8",
2278
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2073 2279
                 "shasum": ""
2074 2280
             },
2075 2281
             "require": {
@@ -2118,7 +2324,11 @@
2118 2324
                 "monolog/monolog": ">=1.0,<1.3-dev",
2119 2325
                 "propel/propel1": "dev-master"
2120 2326
             },
2327
+<<<<<<< HEAD
2121 2328
             "time": "2013-03-26 10:44:36",
2329
+=======
2330
+            "time": "2013-02-23 21:28:36",
2331
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2122 2332
             "type": "library",
2123 2333
             "autoload": {
2124 2334
                 "psr-0": {
@@ -2249,19 +2459,32 @@
2249 2459
             "source": {
2250 2460
                 "type": "git",
2251 2461
                 "url": "https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle.git",
2462
+<<<<<<< HEAD
2252 2463
                 "reference": "9cce71a2cfffa4e3d08a41b6ddd31ef0249a0aea"
2253 2464
             },
2254 2465
             "dist": {
2255 2466
                 "type": "zip",
2256 2467
                 "url": "https://api.github.com/repos/whiteoctober/WhiteOctoberPagerfantaBundle/zipball/9cce71a2cfffa4e3d08a41b6ddd31ef0249a0aea",
2257 2468
                 "reference": "9cce71a2cfffa4e3d08a41b6ddd31ef0249a0aea",
2469
+=======
2470
+                "reference": "f664b66bc9351c6dd24026b3cf4c897705e51b26"
2471
+            },
2472
+            "dist": {
2473
+                "type": "zip",
2474
+                "url": "https://api.github.com/repos/whiteoctober/WhiteOctoberPagerfantaBundle/zipball/f664b66bc9351c6dd24026b3cf4c897705e51b26",
2475
+                "reference": "f664b66bc9351c6dd24026b3cf4c897705e51b26",
2476
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2258 2477
                 "shasum": ""
2259 2478
             },
2260 2479
             "require": {
2261 2480
                 "pagerfanta/pagerfanta": "1.0.*",
2262 2481
                 "symfony/framework-bundle": ">=2.1,<3.0"
2263 2482
             },
2483
+<<<<<<< HEAD
2264 2484
             "time": "2013-03-14 08:58:21",
2485
+=======
2486
+            "time": "2013-02-18 10:43:20",
2487
+>>>>>>> 86d451de31d12560b4aa04cb40581c4ea4f05536
2265 2488
             "type": "symfony-bundle",
2266 2489
             "extra": {
2267 2490
                 "branch-alias": {

+ 0 - 2
src/Muzich/AdminBundle/Controller/DashboardController.php 查看文件

@@ -3,7 +3,6 @@
3 3
 namespace Muzich\AdminBundle\Controller;
4 4
 
5 5
 use Muzich\CoreBundle\lib\Controller;
6
-use Symfony\Component\HttpFoundation\Request;
7 6
 
8 7
 class DashboardController extends Controller
9 8
 {
@@ -13,5 +12,4 @@ class DashboardController extends Controller
13 12
       'base_admin_template' => $this->container->getParameter('admingenerator.base_admin_template'),
14 13
     ));
15 14
   }
16
-  
17 15
 }

+ 1 - 1
src/Muzich/AdminBundle/Resources/views/Admin_userEdit/password.html.twig 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "AdmingeneratorGeneratorBundle::base_admin_assetic_less.html.twig" %}
1
+{% extends "MuzichAdminBundle::base_admin_assetic_less.html.twig" %}
2 2
 
3 3
 {% block stylesheets %}{{ parent() }}{% endblock %}
4 4
 {% block javascripts %}{{ parent() }}{% endblock %}

+ 0 - 0
src/Muzich/AdminBundle/Resources/views/Dashboard/dashboard.html.twig 查看文件


app/Resources/AdmingeneratorGeneratorBundle/views/Dashboard/dashboard.html.twig → src/Muzich/AdminBundle/Resources/views/Dashboard/dashboard.html.twig~HEAD 查看文件


+ 0 - 0
src/Muzich/AdminBundle/Resources/views/Dashboard/welcome.html.twig 查看文件


app/Resources/AdmingeneratorGeneratorBundle/views/Dashboard/welcome.html.twig → src/Muzich/AdminBundle/Resources/views/Dashboard/welcome.html.twig~HEAD 查看文件

@@ -1,4 +1,4 @@
1
-{% extends 'AdmingeneratorGeneratorBundle:Dashboard:dashboard.html.twig' %}
1
+{% extends 'MuzichAdminBundle:Dashboard:dashboard.html.twig' %}
2 2
 
3 3
 {% block title %}Admingenerator{% endblock %}
4 4
 

+ 1 - 1
src/Muzich/AdminBundle/Resources/views/Moderate_commentList/list.html.twig 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "AdmingeneratorGeneratorBundle::base_admin_assetic_less.html.twig" %}
1
+{% extends "MuzichAdminBundle::base_admin_assetic_less.html.twig" %}
2 2
 
3 3
 {% block stylesheets %}{{ parent() }}{% endblock %}
4 4
 

+ 0 - 0
src/Muzich/AdminBundle/Resources/views/base_admin_assetic_less.html.twig 查看文件


app/Resources/AdmingeneratorGeneratorBundle/views/base_admin_assetic_less.html.twig → src/Muzich/AdminBundle/Resources/views/base_admin_assetic_less.html.twig~HEAD 查看文件


+ 1 - 1
src/Muzich/CoreBundle/Command/CheckModerateCommand.php 查看文件

@@ -54,7 +54,7 @@ class CheckModerateCommand extends ContainerAwareCommand
54 54
       
55 55
       $message = \Swift_Message::newInstance()
56 56
           ->setSubject('Muzi.ch: Contrôle modération')
57
-          ->setFrom('noreply@muzi.ch')
57
+          ->setFrom('contact@muzi.ch')
58 58
           ->setTo('sevajol.bastien@gmail.com')
59 59
           ->setBody($this->getContainer()->get('templating')->render('MuzichCoreBundle:Email:checkmoderate.txt.twig', array(
60 60
             'tags'     => $count_tags,

+ 10 - 0
src/Muzich/CoreBundle/Controller/InfoController.php 查看文件

@@ -70,4 +70,14 @@ class InfoController extends Controller
70 70
     ));
71 71
   }
72 72
   
73
+  public function testErrorAction($code)
74
+  {
75
+    if (!is_numeric($code))
76
+    {
77
+      throw new HttpException(404);
78
+    }
79
+    
80
+    return $this->render('TwigBundle:Exception:error'.$code.'.html.twig');
81
+  }
82
+  
73 83
 }

+ 17 - 1
src/Muzich/CoreBundle/Factory/ElementFactory.php 查看文件

@@ -63,7 +63,23 @@ abstract class ElementFactory
63 63
   
64 64
   public function proceedThumbnailUrl()
65 65
   {
66
-    $this->element->setThumbnailUrl(null);
66
+    if (($thumb_url = $this->element->getData(Element::DATA_THUMB_URL)))
67
+    {
68
+      $this->element->setThumbnailUrl($thumb_url);
69
+    }
70
+  }
71
+  
72
+  protected function getJsonDataFromApiWithUrl($url)
73
+  {
74
+    $api_url = curl_init($url);
75
+    
76
+    $options = array(
77
+      CURLOPT_RETURNTRANSFER => true,
78
+      CURLOPT_HTTPHEADER => array('Content-type: application/json')
79
+    );
80
+      
81
+    curl_setopt_array($api_url, $options);
82
+    return json_decode(curl_exec($api_url), true);
67 83
   }
68 84
   
69 85
 }

+ 64 - 0
src/Muzich/CoreBundle/Factory/Elements/Vimeocom.php 查看文件

@@ -0,0 +1,64 @@
1
+<?php
2
+
3
+namespace Muzich\CoreBundle\Factory\Elements;
4
+
5
+use Muzich\CoreBundle\Factory\ElementFactory;
6
+use Muzich\CoreBundle\Entity\Element;
7
+
8
+/**
9
+ * 
10
+ *
11
+ * @author bux
12
+ */
13
+class Vimeocom extends ElementFactory
14
+{
15
+  
16
+  public function retrieveDatas()
17
+  {
18
+    $url_clean = $this->getCleanedUrl();
19
+    $ref_id = null;
20
+    
21
+    // http://vimeo.com/43258820
22
+    if (preg_match("#\/([0-9]+)#", $url_clean, $chaines))
23
+    {
24
+      $ref_id = $chaines[1];
25
+    }
26
+    
27
+    if ($ref_id)
28
+    {
29
+      $this->element->setData(Element::DATA_REF_ID, $ref_id);
30
+      $this->getDataFromApi($ref_id);
31
+    }
32
+  }
33
+  
34
+  protected function getDataFromApi($ref_id)
35
+  {
36
+    $data = $this->getJsonDataFromApiWithUrl('http://vimeo.com/api/v2/video/'.$ref_id.'.json');
37
+    
38
+    if (count($data))
39
+    {
40
+      if (array_key_exists('title', $data[0]))
41
+      {
42
+        $this->element->setData(Element::DATA_TITLE, $data[0]['title']);
43
+      }
44
+      if (array_key_exists('thumbnail_medium', $data[0]))
45
+      {
46
+        $this->element->setData(Element::DATA_THUMB_URL, $data[0]['thumbnail_medium']);
47
+      }
48
+    }
49
+  }
50
+  
51
+  public function proceedEmbedCode()
52
+  {
53
+    if (($ref_id = $this->element->getData(Element::DATA_REF_ID)))
54
+    {
55
+      $width = $this->container->getParameter('vimeo_player_width');
56
+      $height = $this->container->getParameter('vimeo_player_height');
57
+      $this->element->setEmbed(
58
+        '<iframe src="http://player.vimeo.com/video/'.$ref_id.'&autoplay=1&api=1" '
59
+        .'width="'.$width.'" height="'.$height.'" frameborder="0" '
60
+        .'webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'
61
+      );
62
+    }
63
+  }
64
+}

+ 4 - 0
src/Muzich/CoreBundle/Managers/ElementManager.php 查看文件

@@ -13,6 +13,7 @@ use Muzich\CoreBundle\Factory\Elements\Dailymotioncom;
13 13
 use Muzich\CoreBundle\Factory\Elements\Jamendocom;
14 14
 use Muzich\CoreBundle\Factory\Elements\Soundcloudcom;
15 15
 use Muzich\CoreBundle\Factory\Elements\Deezercom;
16
+use Muzich\CoreBundle\Factory\Elements\Vimeocom;
16 17
 
17 18
 /**
18 19
  * 
@@ -167,6 +168,9 @@ class ElementManager
167 168
       case 'deezer.com':
168 169
         return new Deezercom($this->element, $this->container);
169 170
       break;
171
+      case 'vimeo.com':
172
+        return new Vimeocom($this->element, $this->container);
173
+      break;
170 174
       default:
171 175
         throw new \Exception("La Factory n'est pas prise en charge pour ce type.");
172 176
       break;

+ 4 - 0
src/Muzich/CoreBundle/Resources/config/routing.yml 查看文件

@@ -86,6 +86,10 @@ info_cgu:
86 86
   pattern: /info/cgu
87 87
   defaults: { _controller: MuzichCoreBundle:Info:cgu }
88 88
   
89
+test_errors:
90
+  pattern: /info/test-error/{code}
91
+  defaults: { _controller: MuzichCoreBundle:Info:testError }
92
+  
89 93
 ### helper
90 94
 
91 95
 helpbox_bootstrap:

+ 11 - 3
src/Muzich/CoreBundle/Resources/public/css/main.css 查看文件

@@ -306,7 +306,9 @@ td.element_thumbnail
306 306
   background-color: #d5d5d5;
307 307
   padding: 4px;
308 308
   text-align: center;
309
-  width: 140px;
309
+  width: 126px;
310
+  -webkit-border-radius: 3px;
311
+  border-radius: 3px; 
310 312
 }
311 313
 
312 314
 li.element img.element_thumbnail
@@ -1981,8 +1983,8 @@ a.tags_prompt_helpbox
1981 1983
 {
1982 1984
   float: left;
1983 1985
   margin-bottom: 0;
1984
-  margin-left: -6px;
1985
-  margin-top: -11px;
1986
+  margin-left: 0px;
1987
+  margin-top: 5px;
1986 1988
 }
1987 1989
 
1988 1990
 form#address_update input.intext
@@ -1990,6 +1992,12 @@ form#address_update input.intext
1990 1992
   width: 90px;
1991 1993
 }
1992 1994
 
1995
+div#form_add_thumb img
1996
+{
1997
+  width: 100px;
1998
+  height: 100px;
1999
+}
2000
+  
1993 2001
 #know_more
1994 2002
 {
1995 2003
   float: left;

+ 2 - 2
src/Muzich/CoreBundle/Resources/public/js/player/YoutubePlayer.js 查看文件

@@ -12,10 +12,10 @@ function YoutubePlayer(ref_id, object_for_player, finish_callback)
12 12
   
13 13
   var create_player = function()
14 14
   {
15
-    var div_for_iframe = $('<div>').attr('id', _object_for_player.attr('id')+'_iframe');
15
+    var div_for_iframe = $('<div>').attr('id', _object_for_player.attr('id')+'_iframe_'+ref_id);
16 16
     _object_for_player.append(div_for_iframe);
17 17
     
18
-    _yt_player = new YT.Player(_object_for_player.attr('id')+'_iframe', {
18
+    _yt_player = new YT.Player(_object_for_player.attr('id')+'_iframe_'+ref_id, {
19 19
       height  : config_player_youtube_height,
20 20
       width   : '100%',
21 21
       videoId : _ref_id,

+ 14 - 3
src/Muzich/CoreBundle/Resources/views/Layout/head_js.html.twig 查看文件

@@ -2,8 +2,6 @@
2 2
   'js/jquery-1.8.2.prod.js'
3 3
   'js/jquery-ui-1.10.1.custom.min.js'
4 4
   'js/swfobject.js'
5
-  'js/soundcloud/api.js'
6
-  'js/soundcloud/sdk.js'
7 5
   'jplayer/js/jquery.jplayer.min.js'
8 6
   'jplayer/js/jplayer.playlist.min.js'
9 7
   'js/jquery.form-2.14.js'
@@ -18,13 +16,26 @@
18 16
   <script src="{{ asset_url }}"></script>
19 17
 {% endjavascripts %}
20 18
 
19
+<!--<script src="https://w.soundcloud.com/player/api.js"></script>
20
+<script src="https://connect.soundcloud.com/sdk.js"></script>
21
+-->
21 22
 <script>
22 23
   $(document).ready(function(){  
23
-    // Pour le moment on charge toujours yt api
24
+    // Pour le moment on charge toujours ces scripts
24 25
     var tag = document.createElement('script');
25 26
     tag.src = "//www.youtube.com/iframe_api";
26 27
     var firstScriptTag = document.getElementsByTagName('script')[0];
27 28
     firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
29
+    
30
+    var tag = document.createElement('script');
31
+    tag.src = "//w.soundcloud.com/player/api.js";
32
+    var firstScriptTag = document.getElementsByTagName('script')[0];
33
+    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
34
+    
35
+    var tag = document.createElement('script');
36
+    tag.src = "//connect.soundcloud.com/sdk.js";
37
+    var firstScriptTag = document.getElementsByTagName('script')[0];
38
+    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
28 39
   });
29 40
 </script>
30 41
 

+ 19 - 0
src/Muzich/CoreBundle/Resources/views/layout.html.twig 查看文件

@@ -52,5 +52,24 @@
52 52
     
53 53
   </div>
54 54
     
55
+    {% if  app.environment == 'prod' %} 
56
+      <!-- Piwik -->
57
+      <script type="text/javascript"> 
58
+        var _paq = _paq || [];
59
+        _paq.push(['trackPageView']);
60
+        _paq.push(['enableLinkTracking']);
61
+        (function() {
62
+          //var u=(("https:" == document.location.protocol) ? "https" : "http") + "://analytics.muzi.ch//"; # pas de certif sur analytics.muzi.ch pour le moment
63
+          var u=(("https:" == document.location.protocol) ? "http" : "http") + "://analytics.muzi.ch//";
64
+          _paq.push(['setTrackerUrl', u+'piwik.php']);
65
+          _paq.push(['setSiteId', 1]);
66
+          var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript';
67
+          g.defer=true; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
68
+        })();
69
+      
70
+      </script>
71
+      <noscript><p><img src="http://analytics.muzi.ch/piwik.php?idsite=1" style="border:0" alt="" /></p></noscript>
72
+      <!-- End Piwik Code -->
73
+    {% endif %}
55 74
   </body>
56 75
 </html>

+ 14 - 0
src/Muzich/CoreBundle/Tests/ElementFactory/ElementFactoryTest.php 查看文件

@@ -521,6 +521,20 @@ class ElementFactoryTest extends UnitTest
521 521
       'http://www.deezer.com/fr/music/playlist/18701350'
522 522
     ));
523 523
     
524
+    /*
525
+     * Vimeo
526
+     *
527
+     */
528
+    
529
+    $this->assertEquals(array(
530
+      'data_ref_id' => '43258820',
531
+      'data_title'  => 'Punish Yourself',
532
+      'data_thumb_url' => 'http://b.vimeocdn.com/ts/301/282/301282081_200.jpg'
533
+    ),$this->proceed_element_datas_api(
534
+      $bux, 
535
+      'http://vimeo.com/43258820'
536
+    ));
537
+    
524 538
   }
525 539
   
526 540
 }

+ 1 - 0
src/Muzich/IndexBundle/Resources/views/Presubscription/confirm.txt.twig 查看文件

@@ -1,3 +1,4 @@
1 1
 {% autoescape false %}
2 2
   {{ 'mail.presubscription.text'|trans({'%url%':url}, 'text') }}
3
+  {{ 'mail.general.footer'|trans({}, 'text') }}
3 4
 {% endautoescape %}

+ 1 - 1
src/Muzich/UserBundle/Controller/UserController.php 查看文件

@@ -417,7 +417,7 @@ class UserController extends Controller
417 417
 
418 418
       $message = \Swift_Message::newInstance()
419 419
         ->setSubject($subject)
420
-        ->setFrom('noreply@muzi.ch')
420
+        ->setFrom('contact@muzi.ch')
421 421
         ->setTo($email)
422 422
         ->setBody($body);
423 423
 

+ 1 - 0
src/Muzich/UserBundle/Resources/views/User/resetting.email.twig 查看文件

@@ -6,5 +6,6 @@
6 6
       '%url%'      : confirmationUrl,
7 7
       '%username%' : user.username
8 8
     }, 'text') }}
9
+    {{ 'mail.general.footer'|trans({}, 'text') }}
9 10
   {% endautoescape %}
10 11
 {% endblock %}

+ 7 - 0
vendor/autoload.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+
3
+// autoload.php generated by Composer
4
+
5
+require_once __DIR__ . '/composer' . '/autoload_real.php';
6
+
7
+return ComposerAutoloaderInit5c62f4434f25f02a2628d542287e27ca::getLoader();

+ 1 - 0
vendor/bin/doctrine 查看文件

@@ -0,0 +1 @@
1
+../doctrine/orm/bin/doctrine

+ 1 - 0
vendor/bin/doctrine.php 查看文件

@@ -0,0 +1 @@
1
+../doctrine/orm/bin/doctrine.php

+ 46 - 0
vendor/composer/autoload_real.php 查看文件

@@ -0,0 +1,46 @@
1
+<?php
2
+
3
+// autoload_real.php generated by Composer
4
+
5
+class ComposerAutoloaderInit5c62f4434f25f02a2628d542287e27ca
6
+{
7
+    private static $loader;
8
+
9
+    public static function loadClassLoader($class)
10
+    {
11
+        if ('Composer\Autoload\ClassLoader' === $class) {
12
+            require __DIR__ . '/ClassLoader.php';
13
+        }
14
+    }
15
+
16
+    public static function getLoader()
17
+    {
18
+        if (null !== self::$loader) {
19
+            return self::$loader;
20
+        }
21
+
22
+        spl_autoload_register(array('ComposerAutoloaderInit5c62f4434f25f02a2628d542287e27ca', 'loadClassLoader'));
23
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+        spl_autoload_unregister(array('ComposerAutoloaderInit5c62f4434f25f02a2628d542287e27ca', 'loadClassLoader'));
25
+
26
+        $vendorDir = dirname(__DIR__);
27
+        $baseDir = dirname($vendorDir);
28
+
29
+        $map = require __DIR__ . '/autoload_namespaces.php';
30
+        foreach ($map as $namespace => $path) {
31
+            $loader->add($namespace, $path);
32
+        }
33
+
34
+        $classMap = require __DIR__ . '/autoload_classmap.php';
35
+        if ($classMap) {
36
+            $loader->addClassMap($classMap);
37
+        }
38
+
39
+        $loader->register(true);
40
+
41
+        require $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php';
42
+        require $vendorDir . '/kriswallsmith/assetic/src/functions.php';
43
+
44
+        return $loader;
45
+    }
46
+}

文件差異過大導致無法顯示
+ 2371 - 0
vendor/composer/installed.json


+ 55 - 0
vendor/doctrine/common/lib/Doctrine/Common/Version.php 查看文件

@@ -0,0 +1,55 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+namespace Doctrine\Common;
21
+
22
+/**
23
+ * Class to store and retrieve the version of Doctrine
24
+ *
25
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
26
+ * @link    www.doctrine-project.org
27
+ * @since   2.0
28
+ * @version $Revision$
29
+ * @author  Benjamin Eberlei <kontakt@beberlei.de>
30
+ * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
31
+ * @author  Jonathan Wage <jonwage@gmail.com>
32
+ * @author  Roman Borschel <roman@code-factory.org>
33
+ */
34
+class Version
35
+{
36
+    /**
37
+     * Current Doctrine Version
38
+     */
39
+    const VERSION = '2.3.0';
40
+
41
+    /**
42
+     * Compares a Doctrine version with the current one.
43
+     *
44
+     * @param string $version Doctrine version to compare.
45
+     * @return int Returns -1 if older, 0 if it is the same, 1 if version
46
+     *             passed as argument is newer.
47
+     */
48
+    public static function compare($version)
49
+    {
50
+        $currentVersion = str_replace(' ', '', strtolower(self::VERSION));
51
+        $version = str_replace(' ', '', $version);
52
+
53
+        return version_compare($version, $currentVersion);
54
+    }
55
+}

+ 26 - 0
vendor/doctrine/dbal/composer.json 查看文件

@@ -0,0 +1,26 @@
1
+{
2
+    "name": "doctrine/dbal",
3
+    "type": "library","version":"2.3.2",
4
+    "description": "Database Abstraction Layer",
5
+    "keywords": ["dbal", "database", "persistence", "queryobject"],
6
+    "homepage": "http://www.doctrine-project.org",
7
+    "license": "MIT",
8
+    "authors": [
9
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
10
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
11
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
12
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
13
+    ],
14
+    "require": {
15
+        "php": ">=5.3.2",
16
+        "doctrine/common": "2.3.*"
17
+    },
18
+    "autoload": {
19
+        "psr-0": { "Doctrine\\DBAL": "lib/" }
20
+    },
21
+    "extra": {
22
+        "branch-alias": {
23
+            "dev-master": "2.3.x-dev"
24
+        }
25
+    }
26
+}

+ 927 - 0
vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php 查看文件

@@ -0,0 +1,927 @@
1
+<?php
2
+
3
+/*
4
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
+ *
16
+ * This software consists of voluntary contributions made by many individuals
17
+ * and is licensed under the MIT license. For more information, see
18
+ * <http://www.doctrine-project.org>.
19
+ */
20
+
21
+namespace Doctrine\DBAL\Platforms;
22
+
23
+use Doctrine\DBAL\Schema\TableDiff;
24
+use Doctrine\DBAL\DBALException;
25
+use Doctrine\DBAL\Schema\ForeignKeyConstraint;
26
+use Doctrine\DBAL\Schema\Index;
27
+use Doctrine\DBAL\Schema\Table;
28
+
29
+/**
30
+ * The SQLServerPlatform provides the behavior, features and SQL dialect of the
31
+ * Microsoft SQL Server database platform.
32
+ *
33
+ * @since 2.0
34
+ * @author Roman Borschel <roman@code-factory.org>
35
+ * @author Jonathan H. Wage <jonwage@gmail.com>
36
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
37
+ */
38
+class SQLServerPlatform extends AbstractPlatform
39
+{
40
+    /**
41
+     * {@inheritDoc}
42
+     */
43
+    public function getDateDiffExpression($date1, $date2)
44
+    {
45
+        return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')';
46
+    }
47
+
48
+    /**
49
+     * {@inheritDoc}
50
+     */
51
+    public function getDateAddDaysExpression($date, $days)
52
+    {
53
+        return 'DATEADD(day, ' . $days . ', ' . $date . ')';
54
+    }
55
+
56
+    /**
57
+     * {@inheritDoc}
58
+     */
59
+    public function getDateSubDaysExpression($date, $days)
60
+    {
61
+        return 'DATEADD(day, -1 * ' . $days . ', ' . $date . ')';
62
+    }
63
+
64
+    /**
65
+     * {@inheritDoc}
66
+     */
67
+    public function getDateAddMonthExpression($date, $months)
68
+    {
69
+        return 'DATEADD(month, ' . $months . ', ' . $date . ')';
70
+    }
71
+
72
+    /**
73
+     * {@inheritDoc}
74
+     */
75
+    public function getDateSubMonthExpression($date, $months)
76
+    {
77
+        return 'DATEADD(month, -1 * ' . $months . ', ' . $date . ')';
78
+    }
79
+
80
+    /**
81
+     * {@inheritDoc}
82
+     *
83
+     * MsSql prefers "autoincrement" identity columns since sequences can only
84
+     * be emulated with a table.
85
+     */
86
+    public function prefersIdentityColumns()
87
+    {
88
+        return true;
89
+    }
90
+
91
+    /**
92
+     * {@inheritDoc}
93
+     *
94
+     * MsSql supports this through AUTO_INCREMENT columns.
95
+     */
96
+    public function supportsIdentityColumns()
97
+    {
98
+        return true;
99
+    }
100
+
101
+    /**
102
+     * {@inheritDoc}
103
+     */
104
+    public function supportsReleaseSavepoints()
105
+    {
106
+        return false;
107
+    }
108
+
109
+    /**
110
+     * {@inheritDoc}
111
+     */
112
+    public function getCreateDatabaseSQL($name)
113
+    {
114
+        return 'CREATE DATABASE ' . $name;
115
+    }
116
+
117
+    /**
118
+     * {@inheritDoc}
119
+     */
120
+    public function getDropDatabaseSQL($name)
121
+    {
122
+        return 'DROP DATABASE ' . $name;
123
+    }
124
+
125
+    /**
126
+     * {@inheritDoc}
127
+     */
128
+    public function supportsCreateDropDatabase()
129
+    {
130
+        return false;
131
+    }
132
+
133
+    /**
134
+     * {@inheritDoc}
135
+     */
136
+    public function getDropForeignKeySQL($foreignKey, $table)
137
+    {
138
+        if ($foreignKey instanceof ForeignKeyConstraint) {
139
+            $foreignKey = $foreignKey->getQuotedName($this);
140
+        }
141
+
142
+        if ($table instanceof Table) {
143
+            $table = $table->getQuotedName($this);
144
+        }
145
+
146
+        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
147
+    }
148
+
149
+    /**
150
+     * {@inheritDoc}
151
+     */
152
+    public function getDropIndexSQL($index, $table = null)
153
+    {
154
+        if ($index instanceof Index) {
155
+            $index = $index->getQuotedName($this);
156
+        } else if (!is_string($index)) {
157
+            throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
158
+        }
159
+
160
+        if (!isset($table)) {
161
+            return 'DROP INDEX ' . $index;
162
+        }
163
+
164
+        if ($table instanceof Table) {
165
+            $table = $table->getQuotedName($this);
166
+        }
167
+
168
+        return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index')
169
+                    ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . "
170
+                ELSE
171
+                    DROP INDEX " . $index . " ON " . $table;
172
+    }
173
+
174
+    /**
175
+     * {@inheritDoc}
176
+     */
177
+    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
178
+    {
179
+        // @todo does other code breaks because of this?
180
+        // force primary keys to be not null
181
+        foreach ($columns as &$column) {
182
+            if (isset($column['primary']) && $column['primary']) {
183
+                $column['notnull'] = true;
184
+            }
185
+        }
186
+
187
+        $columnListSql = $this->getColumnDeclarationListSQL($columns);
188
+
189
+        if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) {
190
+            foreach ($options['uniqueConstraints'] as $name => $definition) {
191
+                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
192
+            }
193
+        }
194
+
195
+        if (isset($options['primary']) && !empty($options['primary'])) {
196
+            $flags = '';
197
+            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) {
198
+                $flags = ' NONCLUSTERED';
199
+            }
200
+            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')';
201
+        }
202
+
203
+        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
204
+
205
+        $check = $this->getCheckDeclarationSQL($columns);
206
+        if (!empty($check)) {
207
+            $query .= ', ' . $check;
208
+        }
209
+        $query .= ')';
210
+
211
+        $sql[] = $query;
212
+
213
+        if (isset($options['indexes']) && !empty($options['indexes'])) {
214
+            foreach ($options['indexes'] as $index) {
215
+                $sql[] = $this->getCreateIndexSQL($index, $tableName);
216
+            }
217
+        }
218
+
219
+        if (isset($options['foreignKeys'])) {
220
+            foreach ((array) $options['foreignKeys'] as $definition) {
221
+                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
222
+            }
223
+        }
224
+
225
+        return $sql;
226
+    }
227
+
228
+    /**
229
+     * {@inheritDoc}
230
+     */
231
+    public function getCreatePrimaryKeySQL(Index $index, $table)
232
+    {
233
+        $flags = '';
234
+        if ($index->hasFlag('nonclustered')) {
235
+            $flags = ' NONCLUSTERED';
236
+        }
237
+        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')';
238
+    }
239
+
240
+    /**
241
+     * {@inheritDoc}
242
+     */
243
+    public function getUniqueConstraintDeclarationSQL($name, Index $index)
244
+    {
245
+        $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index);
246
+
247
+        $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);
248
+
249
+        return $constraint;
250
+    }
251
+
252
+    /**
253
+     * {@inheritDoc}
254
+     */
255
+    public function getCreateIndexSQL(Index $index, $table)
256
+    {
257
+        $constraint = parent::getCreateIndexSQL($index, $table);
258
+
259
+        if ($index->isUnique() && !$index->isPrimary()) {
260
+            $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);
261
+        }
262
+
263
+        return $constraint;
264
+    }
265
+
266
+    /**
267
+     * {@inheritDoc}
268
+     */
269
+    protected function getCreateIndexSQLFlags(Index $index)
270
+    {
271
+        $type = '';
272
+        if ($index->isUnique()) {
273
+            $type .= 'UNIQUE ';
274
+        }
275
+
276
+        if ($index->hasFlag('clustered')) {
277
+            $type .= 'CLUSTERED ';
278
+        } else if ($index->hasFlag('nonclustered')) {
279
+            $type .= 'NONCLUSTERED ';
280
+        }
281
+
282
+        return $type;
283
+    }
284
+
285
+    /**
286
+     * Extend unique key constraint with required filters
287
+     *
288
+     * @param string $sql
289
+     * @param Index $index
290
+     *
291
+     * @return string
292
+     */
293
+    private function _appendUniqueConstraintDefinition($sql, Index $index)
294
+    {
295
+        $fields = array();
296
+        foreach ($index->getColumns() as $field => $definition) {
297
+            if (!is_array($definition)) {
298
+                $field = $definition;
299
+            }
300
+
301
+            $fields[] = $field . ' IS NOT NULL';
302
+        }
303
+
304
+        return $sql . ' WHERE ' . implode(' AND ', $fields);
305
+    }
306
+
307
+    /**
308
+     * {@inheritDoc}
309
+     */
310
+    public function getAlterTableSQL(TableDiff $diff)
311
+    {
312
+        $queryParts = array();
313
+        $sql = array();
314
+        $columnSql = array();
315
+
316
+        foreach ($diff->addedColumns as $column) {
317
+            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
318
+                continue;
319
+            }
320
+
321
+            $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
322
+        }
323
+
324
+        foreach ($diff->removedColumns as $column) {
325
+            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
326
+                continue;
327
+            }
328
+
329
+            $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);
330
+        }
331
+
332
+        foreach ($diff->changedColumns as $columnDiff) {
333
+            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
334
+                continue;
335
+            }
336
+
337
+            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
338
+            $column = $columnDiff->column;
339
+            $queryParts[] = 'ALTER COLUMN ' .
340
+                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
341
+        }
342
+
343
+        foreach ($diff->renamedColumns as $oldColumnName => $column) {
344
+            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
345
+                continue;
346
+            }
347
+
348
+            $sql[] = "sp_RENAME '". $diff->name. ".". $oldColumnName . "' , '".$column->getQuotedName($this)."', 'COLUMN'";
349
+            $queryParts[] = 'ALTER COLUMN ' .
350
+                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
351
+        }
352
+
353
+        $tableSql = array();
354
+
355
+        if ($this->onSchemaAlterTable($diff, $tableSql)) {
356
+            return array_merge($tableSql, $columnSql);
357
+        }
358
+
359
+        foreach ($queryParts as $query) {
360
+            $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
361
+        }
362
+
363
+        $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
364
+
365
+        if ($diff->newName !== false) {
366
+            $sql[] = "sp_RENAME '" . $diff->name . "', '" . $diff->newName . "'";
367
+        }
368
+
369
+        return array_merge($sql, $tableSql, $columnSql);
370
+    }
371
+
372
+    /**
373
+     * {@inheritDoc}
374
+     */
375
+    public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
376
+    {
377
+        return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES';
378
+    }
379
+
380
+    /**
381
+     * {@inheritDoc}
382
+     */
383
+    public function getShowDatabasesSQL()
384
+    {
385
+        return 'SHOW DATABASES';
386
+    }
387
+
388
+    /**
389
+     * {@inheritDoc}
390
+     */
391
+    public function getListTablesSQL()
392
+    {
393
+        // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams
394
+        return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name";
395
+    }
396
+
397
+    /**
398
+     * {@inheritDoc}
399
+     */
400
+    public function getListTableColumnsSQL($table, $database = null)
401
+    {
402
+        return "exec sp_columns @table_name = '" . $table . "'";
403
+    }
404
+
405
+    /**
406
+     * {@inheritDoc}
407
+     */
408
+    public function getListTableForeignKeysSQL($table, $database = null)
409
+    {
410
+        return "SELECT f.name AS ForeignKey,
411
+                SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName,
412
+                OBJECT_NAME (f.parent_object_id) AS TableName,
413
+                COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName,
414
+                SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName,
415
+                OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
416
+                COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName,
417
+                f.delete_referential_action_desc,
418
+                f.update_referential_action_desc
419
+                FROM sys.foreign_keys AS f
420
+                INNER JOIN sys.foreign_key_columns AS fc
421
+                INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id
422
+                ON f.OBJECT_ID = fc.constraint_object_id
423
+                WHERE OBJECT_NAME (f.parent_object_id) = '" . $table . "'";
424
+    }
425
+
426
+    /**
427
+     * {@inheritDoc}
428
+     */
429
+    public function getListTableIndexesSQL($table, $currentDatabase = null)
430
+    {
431
+        return "exec sp_helpindex '" . $table . "'";
432
+    }
433
+
434
+    /**
435
+     * {@inheritDoc}
436
+     */
437
+    public function getCreateViewSQL($name, $sql)
438
+    {
439
+        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
440
+    }
441
+
442
+    /**
443
+     * {@inheritDoc}
444
+     */
445
+    public function getListViewsSQL($database)
446
+    {
447
+        return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name";
448
+    }
449
+
450
+    /**
451
+     * {@inheritDoc}
452
+     */
453
+    public function getDropViewSQL($name)
454
+    {
455
+        return 'DROP VIEW ' . $name;
456
+    }
457
+
458
+    /**
459
+     * {@inheritDoc}
460
+     */
461
+    public function getRegexpExpression()
462
+    {
463
+        return 'RLIKE';
464
+    }
465
+
466
+    /**
467
+     * {@inheritDoc}
468
+     */
469
+    public function getGuidExpression()
470
+    {
471
+        return 'UUID()';
472
+    }
473
+
474
+    /**
475
+     * {@inheritDoc}
476
+     */
477
+    public function getLocateExpression($str, $substr, $startPos = false)
478
+    {
479
+        if ($startPos == false) {
480
+            return 'CHARINDEX(' . $substr . ', ' . $str . ')';
481
+        }
482
+
483
+        return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')';
484
+    }
485
+
486
+    /**
487
+     * {@inheritDoc}
488
+     */
489
+    public function getModExpression($expression1, $expression2)
490
+    {
491
+        return $expression1 . ' % ' . $expression2;
492
+    }
493
+
494
+    /**
495
+     * {@inheritDoc}
496
+     */
497
+    public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
498
+    {
499
+        if ( ! $char) {
500
+            switch ($pos) {
501
+                case self::TRIM_LEADING:
502
+                    $trimFn = 'LTRIM';
503
+                    break;
504
+
505
+                case self::TRIM_TRAILING:
506
+                    $trimFn = 'RTRIM';
507
+                    break;
508
+
509
+                default:
510
+                    return 'LTRIM(RTRIM(' . $str . '))';
511
+            }
512
+
513
+            return $trimFn . '(' . $str . ')';
514
+        }
515
+
516
+        /** Original query used to get those expressions
517
+          declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x';
518
+          declare @pat varchar(10) = '%[^' + @trim_char + ']%';
519
+          select @c as string
520
+          , @trim_char as trim_char
521
+          , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading
522
+          , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing
523
+          , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both;
524
+         */
525
+        $pattern = "'%[^' + $char + ']%'";
526
+
527
+        if ($pos == self::TRIM_LEADING) {
528
+            return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)';
529
+        }
530
+
531
+        if ($pos == self::TRIM_TRAILING) {
532
+            return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))';
533
+        }
534
+
535
+        return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))';
536
+    }
537
+
538
+    /**
539
+     * {@inheritDoc}
540
+     */
541
+    public function getConcatExpression()
542
+    {
543
+        $args = func_get_args();
544
+
545
+        return '(' . implode(' + ', $args) . ')';
546
+    }
547
+
548
+    public function getListDatabasesSQL()
549
+    {
550
+        return 'SELECT * FROM SYS.DATABASES';
551
+    }
552
+
553
+    /**
554
+     * {@inheritDoc}
555
+     */
556
+    public function getSubstringExpression($value, $from, $length = null)
557
+    {
558
+        if (!is_null($length)) {
559
+            return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
560
+        }
561
+
562
+        return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)';
563
+    }
564
+
565
+    /**
566
+     * {@inheritDoc}
567
+     */
568
+    public function getLengthExpression($column)
569
+    {
570
+        return 'LEN(' . $column . ')';
571
+    }
572
+
573
+    /**
574
+     * {@inheritDoc}
575
+     */
576
+    public function getSetTransactionIsolationSQL($level)
577
+    {
578
+        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
579
+    }
580
+
581
+    /**
582
+     * {@inheritDoc}
583
+     */
584
+    public function getIntegerTypeDeclarationSQL(array $field)
585
+    {
586
+        return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
587
+    }
588
+
589
+    /**
590
+     * {@inheritDoc}
591
+     */
592
+    public function getBigIntTypeDeclarationSQL(array $field)
593
+    {
594
+        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
595
+    }
596
+
597
+    /**
598
+     * {@inheritDoc}
599
+     */
600
+    public function getSmallIntTypeDeclarationSQL(array $field)
601
+    {
602
+        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
603
+    }
604
+
605
+    /**
606
+     * {@inheritDoc}
607
+     */
608
+    public function getGuidTypeDeclarationSQL(array $field)
609
+    {
610
+        return 'UNIQUEIDENTIFIER';
611
+    }
612
+
613
+    /**
614
+     * {@inheritDoc}
615
+     */
616
+    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
617
+    {
618
+        return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)');
619
+    }
620
+
621
+    /**
622
+     * {@inheritDoc}
623
+     */
624
+    public function getClobTypeDeclarationSQL(array $field)
625
+    {
626
+        return 'TEXT';
627
+    }
628
+
629
+    /**
630
+     * {@inheritDoc}
631
+     */
632
+    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
633
+    {
634
+        return (!empty($columnDef['autoincrement'])) ? ' IDENTITY' : '';
635
+    }
636
+
637
+    /**
638
+     * {@inheritDoc}
639
+     */
640
+    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
641
+    {
642
+        return 'DATETIME';
643
+    }
644
+
645
+    /**
646
+     * {@inheritDoc}
647
+     */
648
+    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
649
+    {
650
+        return 'DATETIME';
651
+    }
652
+
653
+    /**
654
+     * {@inheritDoc}
655
+     */
656
+    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
657
+    {
658
+        return 'DATETIME';
659
+    }
660
+
661
+    /**
662
+     * {@inheritDoc}
663
+     */
664
+    public function getBooleanTypeDeclarationSQL(array $field)
665
+    {
666
+        return 'BIT';
667
+    }
668
+
669
+    /**
670
+     * {@inheritDoc}
671
+     *
672
+     * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html
673
+     */
674
+    protected function doModifyLimitQuery($query, $limit, $offset = null)
675
+    {
676
+        if ($limit > 0) {
677
+            if ($offset == 0) {
678
+                $query = preg_replace('/^(SELECT\s(DISTINCT\s)?)/i', '\1TOP ' . $limit . ' ', $query);
679
+            } else {
680
+                $orderby = stristr($query, 'ORDER BY');
681
+
682
+                if ( ! $orderby) {
683
+                    $over = 'ORDER BY (SELECT 0)';
684
+                } else {
685
+                    $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby);
686
+                }
687
+
688
+                // Remove ORDER BY clause from $query
689
+                $query = preg_replace('/\s+ORDER BY(.*)/', '', $query);
690
+                $query = preg_replace('/^SELECT\s/', '', $query);
691
+
692
+                $start = $offset + 1;
693
+                $end = $offset + $limit;
694
+
695
+                $query = "SELECT * FROM (SELECT ROW_NUMBER() OVER ($over) AS doctrine_rownum, $query) AS doctrine_tbl WHERE doctrine_rownum BETWEEN $start AND $end";
696
+            }
697
+        }
698
+
699
+        return $query;
700
+    }
701
+
702
+    /**
703
+     * {@inheritDoc}
704
+     */
705
+    public function supportsLimitOffset()
706
+    {
707
+        return false;
708
+    }
709
+
710
+    /**
711
+     * {@inheritDoc}
712
+     */
713
+    public function convertBooleans($item)
714
+    {
715
+        if (is_array($item)) {
716
+            foreach ($item as $key => $value) {
717
+                if (is_bool($value) || is_numeric($item)) {
718
+                    $item[$key] = ($value) ? 1 : 0;
719
+                }
720
+            }
721
+        } else if (is_bool($item) || is_numeric($item)) {
722
+            $item = ($item) ? 1 : 0;
723
+        }
724
+
725
+        return $item;
726
+    }
727
+
728
+    /**
729
+     * {@inheritDoc}
730
+     */
731
+    public function getCreateTemporaryTableSnippetSQL()
732
+    {
733
+        return "CREATE TABLE";
734
+    }
735
+
736
+    /**
737
+     * {@inheritDoc}
738
+     */
739
+    public function getTemporaryTableName($tableName)
740
+    {
741
+        return '#' . $tableName;
742
+    }
743
+
744
+    /**
745
+     * {@inheritDoc}
746
+     */
747
+    public function getDateTimeFormatString()
748
+    {
749
+        return 'Y-m-d H:i:s.000';
750
+    }
751
+
752
+    /**
753
+     * {@inheritDoc}
754
+     */
755
+    public function getDateFormatString()
756
+    {
757
+        return 'Y-m-d H:i:s.000';
758
+    }
759
+
760
+    /**
761
+     * {@inheritDoc}
762
+     */
763
+    public function getTimeFormatString()
764
+    {
765
+        return 'Y-m-d H:i:s.000';
766
+    }
767
+
768
+    /**
769
+     * {@inheritDoc}
770
+     */
771
+    public function getDateTimeTzFormatString()
772
+    {
773
+        return $this->getDateTimeFormatString();
774
+    }
775
+
776
+    /**
777
+     * {@inheritDoc}
778
+     */
779
+    public function getName()
780
+    {
781
+        return 'mssql';
782
+    }
783
+
784
+    /**
785
+     * {@inheritDoc}
786
+     */
787
+    protected function initializeDoctrineTypeMappings()
788
+    {
789
+        $this->doctrineTypeMapping = array(
790
+            'bigint' => 'bigint',
791
+            'numeric' => 'decimal',
792
+            'bit' => 'boolean',
793
+            'smallint' => 'smallint',
794
+            'decimal' => 'decimal',
795
+            'smallmoney' => 'integer',
796
+            'int' => 'integer',
797
+            'tinyint' => 'smallint',
798
+            'money' => 'integer',
799
+            'float' => 'float',
800
+            'real' => 'float',
801
+            'double' => 'float',
802
+            'double precision' => 'float',
803
+            'datetimeoffset' => 'datetimetz',
804
+            'smalldatetime' => 'datetime',
805
+            'datetime' => 'datetime',
806
+            'char' => 'string',
807
+            'varchar' => 'string',
808
+            'text' => 'text',
809
+            'nchar' => 'string',
810
+            'nvarchar' => 'string',
811
+            'ntext' => 'text',
812
+            'binary' => 'text',
813
+            'varbinary' => 'blob',
814
+            'image' => 'text',
815
+            'uniqueidentifier' => 'guid',
816
+        );
817
+    }
818
+
819
+    /**
820
+     * {@inheritDoc}
821
+     */
822
+    public function createSavePoint($savepoint)
823
+    {
824
+        return 'SAVE TRANSACTION ' . $savepoint;
825
+    }
826
+
827
+    /**
828
+     * {@inheritDoc}
829
+     */
830
+    public function releaseSavePoint($savepoint)
831
+    {
832
+        return '';
833
+    }
834
+
835
+    /**
836
+     * {@inheritDoc}
837
+     */
838
+    public function rollbackSavePoint($savepoint)
839
+    {
840
+        return 'ROLLBACK TRANSACTION ' . $savepoint;
841
+    }
842
+
843
+    /**
844
+     * {@inheritDoc}
845
+     */
846
+    public function appendLockHint($fromClause, $lockMode)
847
+    {
848
+        // @todo coorect
849
+        if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) {
850
+            return $fromClause . ' WITH (tablockx)';
851
+        }
852
+
853
+        if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) {
854
+            return $fromClause . ' WITH (tablockx)';
855
+        }
856
+
857
+        return $fromClause;
858
+    }
859
+
860
+    /**
861
+     * {@inheritDoc}
862
+     */
863
+    public function getForUpdateSQL()
864
+    {
865
+        return ' ';
866
+    }
867
+
868
+    /**
869
+     * {@inheritDoc}
870
+     */
871
+    protected function getReservedKeywordsClass()
872
+    {
873
+        return 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords';
874
+    }
875
+
876
+    /**
877
+     * {@inheritDoc}
878
+     */
879
+    public function quoteSingleIdentifier($str)
880
+    {
881
+        return "[" . str_replace("]", "][", $str) . "]";
882
+    }
883
+
884
+    /**
885
+     * {@inheritDoc}
886
+     */
887
+    public function getTruncateTableSQL($tableName, $cascade = false)
888
+    {
889
+        return 'TRUNCATE TABLE '.$tableName;
890
+    }
891
+
892
+    /**
893
+     * {@inheritDoc}
894
+     */
895
+    public function getBlobTypeDeclarationSQL(array $field)
896
+    {
897
+        return 'VARBINARY(MAX)';
898
+    }
899
+
900
+    /**
901
+     * {@inheritDoc}
902
+     */
903
+    public function getDefaultValueDeclarationSQL($field)
904
+    {
905
+        if ( ! isset($field['default'])) {
906
+            return empty($field['notnull']) ? ' NULL' : '';
907
+        }
908
+
909
+        if ( ! isset($field['type'])) {
910
+            return " DEFAULT '" . $field['default'] . "'";
911
+        }
912
+
913
+        if (in_array((string) $field['type'], array('Integer', 'BigInteger', 'SmallInteger'))) {
914
+            return " DEFAULT " . $field['default'];
915
+        }
916
+
917
+        if ((string) $field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) {
918
+            return " DEFAULT " . $this->getCurrentTimestampSQL();
919
+        }
920
+
921
+        if ((string) $field['type'] == 'Boolean') {
922
+            return " DEFAULT '" . $this->convertBooleans($field['default']) . "'";
923
+        }
924
+
925
+        return " DEFAULT '" . $field['default'] . "'";
926
+    }
927
+}

+ 183 - 0
vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php 查看文件

@@ -0,0 +1,183 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+
21
+namespace Doctrine\DBAL;
22
+
23
+use Doctrine\DBAL\Connection;
24
+
25
+/**
26
+ * Utility class that parses sql statements with regard to types and parameters.
27
+ *
28
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
29
+ * @link        www.doctrine-project.com
30
+ * @since       2.0
31
+ * @author      Benjamin Eberlei <kontakt@beberlei.de>
32
+ */
33
+class SQLParserUtils
34
+{
35
+    /**
36
+     * Get an array of the placeholders in an sql statements as keys and their positions in the query string.
37
+     *
38
+     * Returns an integer => integer pair (indexed from zero) for a positional statement
39
+     * and a string => int[] pair for a named statement.
40
+     *
41
+     * @param string $statement
42
+     * @param bool $isPositional
43
+     * @return array
44
+     */
45
+    static public function getPlaceholderPositions($statement, $isPositional = true)
46
+    {
47
+        $match = ($isPositional) ? '?' : ':';
48
+        if (strpos($statement, $match) === false) {
49
+            return array();
50
+        }
51
+
52
+        $count = 0;
53
+        $inLiteral = false; // a valid query never starts with quotes
54
+        $stmtLen = strlen($statement);
55
+        $paramMap = array();
56
+        for ($i = 0; $i < $stmtLen; $i++) {
57
+            if ($statement[$i] == $match && !$inLiteral && ($isPositional || $statement[$i+1] != '=')) {
58
+                // real positional parameter detected
59
+                if ($isPositional) {
60
+                    $paramMap[$count] = $i;
61
+                } else {
62
+                    $name = "";
63
+                    // TODO: Something faster/better to match this than regex?
64
+                    for ($j = $i + 1; ($j < $stmtLen && preg_match('(([a-zA-Z0-9_]{1}))', $statement[$j])); $j++) {
65
+                        $name .= $statement[$j];
66
+                    }
67
+                    $paramMap[$i] = $name; // named parameters can be duplicated!
68
+                    $i = $j;
69
+                }
70
+                ++$count;
71
+            } else if ($statement[$i] == "'" || $statement[$i] == '"') {
72
+                $inLiteral = ! $inLiteral; // switch state!
73
+            }
74
+        }
75
+
76
+        return $paramMap;
77
+    }
78
+
79
+    /**
80
+     * For a positional query this method can rewrite the sql statement with regard to array parameters.
81
+     *
82
+     * @param string    $query  The SQL query to execute.
83
+     * @param array     $params The parameters to bind to the query.
84
+     * @param array     $types  The types the previous parameters are in.
85
+     * 
86
+     * @return array
87
+     */
88
+    static public function expandListParameters($query, $params, $types)
89
+    {
90
+        $isPositional   = is_int(key($params));
91
+        $arrayPositions = array();
92
+        $bindIndex      = -1;
93
+
94
+        foreach ($types as $name => $type) {
95
+            ++$bindIndex;
96
+
97
+            if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) {
98
+                continue;
99
+            }
100
+
101
+            if ($isPositional) {
102
+                $name = $bindIndex;
103
+            }
104
+
105
+            $arrayPositions[$name] = false;
106
+        }
107
+
108
+        if (( ! $arrayPositions && $isPositional) || (count($params) != count($types))) {
109
+            return array($query, $params, $types);
110
+        }
111
+
112
+        $paramPos = self::getPlaceholderPositions($query, $isPositional);
113
+
114
+        if ($isPositional) {
115
+            $paramOffset = 0;
116
+            $queryOffset = 0;
117
+
118
+            foreach ($paramPos as $needle => $needlePos) {
119
+                if ( ! isset($arrayPositions[$needle])) {
120
+                    continue;
121
+                }
122
+
123
+                $needle    += $paramOffset;
124
+                $needlePos += $queryOffset;
125
+                $count      = count($params[$needle]);
126
+
127
+                $params = array_merge(
128
+                    array_slice($params, 0, $needle),
129
+                    $params[$needle],
130
+                    array_slice($params, $needle + 1)
131
+                );
132
+
133
+                $types = array_merge(
134
+                    array_slice($types, 0, $needle),
135
+                    array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET), // array needles are at PDO::PARAM_* + 100
136
+                    array_slice($types, $needle + 1)
137
+                );
138
+
139
+                $expandStr  = implode(", ", array_fill(0, $count, "?"));
140
+                $query      = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1);
141
+
142
+                $paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle.
143
+                $queryOffset += (strlen($expandStr) - 1);
144
+            }
145
+
146
+            return array($query, $params, $types);
147
+        }
148
+
149
+
150
+        $queryOffset = 0;
151
+        $typesOrd    = array();
152
+        $paramsOrd   = array();
153
+
154
+        foreach ($paramPos as $pos => $paramName) {
155
+            $paramLen   = strlen($paramName) + 1;
156
+            $value      = $params[$paramName];
157
+
158
+            if ( ! isset($arrayPositions[$paramName])) {
159
+                $pos         += $queryOffset;
160
+                $queryOffset -= ($paramLen - 1);
161
+                $paramsOrd[]  = $value;
162
+                $typesOrd[]   = $types[$paramName];
163
+                $query        = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen));
164
+            
165
+                continue;
166
+            }
167
+
168
+            $count      = count($value);
169
+            $expandStr  = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : '?';
170
+
171
+            foreach ($value as $val) {
172
+                $paramsOrd[] = $val;
173
+                $typesOrd[]  = $types[$paramName] - Connection::ARRAY_PARAM_OFFSET;
174
+            }
175
+
176
+            $pos         += $queryOffset;
177
+            $queryOffset += (strlen($expandStr) - $paramLen);
178
+            $query        = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen));
179
+        }
180
+
181
+        return array($query, $paramsOrd, $typesOrd);
182
+    }
183
+}

+ 55 - 0
vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php 查看文件

@@ -0,0 +1,55 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+namespace Doctrine\DBAL;
21
+
22
+/**
23
+ * Class to store and retrieve the version of Doctrine
24
+ *
25
+ * 
26
+ * @link    www.doctrine-project.org
27
+ * @since   2.0
28
+ * @version $Revision$
29
+ * @author  Benjamin Eberlei <kontakt@beberlei.de>
30
+ * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
31
+ * @author  Jonathan Wage <jonwage@gmail.com>
32
+ * @author  Roman Borschel <roman@code-factory.org>
33
+ */
34
+class Version
35
+{
36
+    /**
37
+     * Current Doctrine Version
38
+     */
39
+    const VERSION = '2.3.2';
40
+
41
+    /**
42
+     * Compares a Doctrine version with the current one.
43
+     *
44
+     * @param string $version Doctrine version to compare.
45
+     * @return int Returns -1 if older, 0 if it is the same, 1 if version
46
+     *             passed as argument is newer.
47
+     */
48
+    public static function compare($version)
49
+    {
50
+        $currentVersion = str_replace(' ', '', strtolower(self::VERSION));
51
+        $version = str_replace(' ', '', $version);
52
+
53
+        return version_compare($version, $currentVersion);
54
+    }
55
+}

+ 245 - 0
vendor/doctrine/dbal/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php 查看文件

@@ -0,0 +1,245 @@
1
+<?php
2
+
3
+namespace Doctrine\Tests\DBAL\Platforms;
4
+
5
+use Doctrine\DBAL\Platforms\SQLServer2008Platform;
6
+use Doctrine\DBAL\Types\Type;
7
+
8
+class SQLServerPlatformTest extends AbstractPlatformTestCase
9
+{
10
+    public function createPlatform()
11
+    {
12
+        return new SQLServer2008Platform;
13
+    }
14
+
15
+    public function getGenerateTableSql()
16
+    {
17
+        return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test NVARCHAR(255) NULL, PRIMARY KEY (id))';
18
+    }
19
+
20
+    public function getGenerateTableWithMultiColumnUniqueIndexSql()
21
+    {
22
+        return array(
23
+            'CREATE TABLE test (foo NVARCHAR(255) NULL, bar NVARCHAR(255) NULL)',
24
+            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar) WHERE foo IS NOT NULL AND bar IS NOT NULL'
25
+        );
26
+    }
27
+
28
+    public function getGenerateAlterTableSql()
29
+    {
30
+        return array(
31
+            'ALTER TABLE mytable ADD quota INT NULL',
32
+            'ALTER TABLE mytable DROP COLUMN foo',
33
+            'ALTER TABLE mytable ALTER COLUMN baz NVARCHAR(255) DEFAULT \'def\' NOT NULL',
34
+            'ALTER TABLE mytable ALTER COLUMN bloo BIT DEFAULT \'0\' NOT NULL',
35
+            "sp_RENAME 'mytable', 'userlist'",
36
+        );
37
+    }
38
+
39
+    public function testGeneratesSqlSnippets()
40
+    {
41
+        $this->assertEquals('RLIKE', $this->_platform->getRegexpExpression(), 'Regular expression operator is not correct');
42
+        $this->assertEquals('"', $this->_platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
43
+        $this->assertEquals('(column1 + column2 + column3)', $this->_platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
44
+    }
45
+
46
+    public function testGeneratesTransactionsCommands()
47
+    {
48
+        $this->assertEquals(
49
+                'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
50
+                $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED)
51
+        );
52
+        $this->assertEquals(
53
+                'SET TRANSACTION ISOLATION LEVEL READ COMMITTED',
54
+                $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED)
55
+        );
56
+        $this->assertEquals(
57
+                'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ',
58
+                $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ)
59
+        );
60
+        $this->assertEquals(
61
+                'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
62
+                $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE)
63
+        );
64
+    }
65
+
66
+    public function testGeneratesDDLSnippets()
67
+    {
68
+        $dropDatabaseExpectation = 'DROP DATABASE foobar';
69
+
70
+        $this->assertEquals('SHOW DATABASES', $this->_platform->getShowDatabasesSQL());
71
+        $this->assertEquals('CREATE DATABASE foobar', $this->_platform->getCreateDatabaseSQL('foobar'));
72
+        $this->assertEquals($dropDatabaseExpectation, $this->_platform->getDropDatabaseSQL('foobar'));
73
+        $this->assertEquals('DROP TABLE foobar', $this->_platform->getDropTableSQL('foobar'));
74
+    }
75
+
76
+    public function testGeneratesTypeDeclarationForIntegers()
77
+    {
78
+        $this->assertEquals(
79
+                'INT',
80
+                $this->_platform->getIntegerTypeDeclarationSQL(array())
81
+        );
82
+        $this->assertEquals(
83
+                'INT IDENTITY',
84
+                $this->_platform->getIntegerTypeDeclarationSQL(array('autoincrement' => true)
85
+        ));
86
+        $this->assertEquals(
87
+                'INT IDENTITY',
88
+                $this->_platform->getIntegerTypeDeclarationSQL(
89
+                        array('autoincrement' => true, 'primary' => true)
90
+        ));
91
+    }
92
+
93
+    public function testGeneratesTypeDeclarationsForStrings()
94
+    {
95
+        $this->assertEquals(
96
+                'NCHAR(10)',
97
+                $this->_platform->getVarcharTypeDeclarationSQL(
98
+                        array('length' => 10, 'fixed' => true)
99
+        ));
100
+        $this->assertEquals(
101
+                'NVARCHAR(50)',
102
+                $this->_platform->getVarcharTypeDeclarationSQL(array('length' => 50)),
103
+                'Variable string declaration is not correct'
104
+        );
105
+        $this->assertEquals(
106
+                'NVARCHAR(255)',
107
+                $this->_platform->getVarcharTypeDeclarationSQL(array()),
108
+                'Long string declaration is not correct'
109
+        );
110
+    }
111
+
112
+    public function testPrefersIdentityColumns()
113
+    {
114
+        $this->assertTrue($this->_platform->prefersIdentityColumns());
115
+    }
116
+
117
+    public function testSupportsIdentityColumns()
118
+    {
119
+        $this->assertTrue($this->_platform->supportsIdentityColumns());
120
+    }
121
+
122
+    public function testDoesNotSupportSavePoints()
123
+    {
124
+        $this->assertTrue($this->_platform->supportsSavepoints());
125
+    }
126
+
127
+    public function getGenerateIndexSql()
128
+    {
129
+        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
130
+    }
131
+
132
+    public function getGenerateUniqueIndexSql()
133
+    {
134
+        return 'CREATE UNIQUE INDEX index_name ON test (test, test2) WHERE test IS NOT NULL AND test2 IS NOT NULL';
135
+    }
136
+
137
+    public function getGenerateForeignKeySql()
138
+    {
139
+        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
140
+    }
141
+
142
+    public function testModifyLimitQuery()
143
+    {
144
+        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
145
+        $this->assertEquals('SELECT TOP 10 * FROM user', $sql);
146
+    }
147
+
148
+    public function testModifyLimitQueryWithEmptyOffset()
149
+    {
150
+        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10);
151
+        $this->assertEquals('SELECT TOP 10 * FROM user', $sql);
152
+    }
153
+
154
+    public function testModifyLimitQueryWithOffset()
155
+    {
156
+        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10, 5);
157
+        $this->assertEquals('SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY username DESC) AS doctrine_rownum, * FROM user) AS doctrine_tbl WHERE doctrine_rownum BETWEEN 6 AND 15', $sql);
158
+    }
159
+
160
+    public function testModifyLimitQueryWithAscOrderBy()
161
+    {
162
+        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user ORDER BY username ASC', 10);
163
+        $this->assertEquals('SELECT TOP 10 * FROM user ORDER BY username ASC', $sql);
164
+    }
165
+
166
+    public function testModifyLimitQueryWithDescOrderBy()
167
+    {
168
+        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10);
169
+        $this->assertEquals('SELECT TOP 10 * FROM user ORDER BY username DESC', $sql);
170
+    }
171
+
172
+    /**
173
+     * @group DDC-1360
174
+     */
175
+    public function testQuoteIdentifier()
176
+    {
177
+        $this->assertEquals('[fo][o]', $this->_platform->quoteIdentifier('fo]o'));
178
+        $this->assertEquals('[test]', $this->_platform->quoteIdentifier('test'));
179
+        $this->assertEquals('[test].[test]', $this->_platform->quoteIdentifier('test.test'));
180
+    }
181
+
182
+    /**
183
+     * @group DDC-1360
184
+     */
185
+    public function testQuoteSingleIdentifier()
186
+    {
187
+        $this->assertEquals('[fo][o]', $this->_platform->quoteSingleIdentifier('fo]o'));
188
+        $this->assertEquals('[test]', $this->_platform->quoteSingleIdentifier('test'));
189
+        $this->assertEquals('[test.test]', $this->_platform->quoteSingleIdentifier('test.test'));
190
+    }
191
+
192
+    /**
193
+     * @group DBAL-220
194
+     */
195
+    public function testCreateClusteredIndex()
196
+    {
197
+        $idx = new \Doctrine\DBAL\Schema\Index('idx', array('id'));
198
+        $idx->addFlag('clustered');
199
+        $this->assertEquals('CREATE CLUSTERED INDEX idx ON tbl (id)', $this->_platform->getCreateIndexSQL($idx, 'tbl'));
200
+    }
201
+
202
+    /**
203
+     * @group DBAL-220
204
+     */
205
+    public function testCreateNonClusteredPrimaryKeyInTable()
206
+    {
207
+        $table = new \Doctrine\DBAL\Schema\Table("tbl");
208
+        $table->addColumn("id", "integer");
209
+        $table->setPrimaryKey(Array("id"));
210
+        $table->getIndex('primary')->addFlag('nonclustered');
211
+
212
+        $this->assertEquals(array('CREATE TABLE tbl (id INT NOT NULL, PRIMARY KEY NONCLUSTERED (id))'), $this->_platform->getCreateTableSQL($table));
213
+    }
214
+
215
+    /**
216
+     * @group DBAL-220
217
+     */
218
+    public function testCreateNonClusteredPrimaryKey()
219
+    {
220
+        $idx = new \Doctrine\DBAL\Schema\Index('idx', array('id'), false, true);
221
+        $idx->addFlag('nonclustered');
222
+        $this->assertEquals('ALTER TABLE tbl ADD PRIMARY KEY NONCLUSTERED (id)', $this->_platform->getCreatePrimaryKeySQL($idx, 'tbl'));
223
+    }
224
+
225
+    public function testAlterAddPrimaryKey()
226
+    {
227
+        $idx = new \Doctrine\DBAL\Schema\Index('idx', array('id'), false, true);
228
+        $this->assertEquals('ALTER TABLE tbl ADD PRIMARY KEY (id)', $this->_platform->getCreateIndexSQL($idx, 'tbl'));
229
+    }
230
+
231
+    protected function getQuotedColumnInPrimaryKeySQL()
232
+    {
233
+        return array(
234
+            'CREATE TABLE [quoted] ([key] NVARCHAR(255) NOT NULL, PRIMARY KEY ([key]))',
235
+        );
236
+    }
237
+
238
+    protected function getQuotedColumnInIndexSQL()
239
+    {
240
+        return array(
241
+            'CREATE TABLE [quoted] ([key] NVARCHAR(255) NOT NULL)',
242
+            'CREATE INDEX IDX_22660D028A90ABA9 ON [quoted] ([key])',
243
+        );
244
+    }
245
+}

+ 252 - 0
vendor/doctrine/dbal/tests/Doctrine/Tests/DBAL/SQLParserUtilsTest.php 查看文件

@@ -0,0 +1,252 @@
1
+<?php
2
+
3
+namespace Doctrine\Tests\DBAL;
4
+
5
+use Doctrine\DBAL\Connection;
6
+use Doctrine\DBAL\SQLParserUtils;
7
+
8
+require_once __DIR__ . '/../TestInit.php';
9
+
10
+/**
11
+ * @group DBAL-78
12
+ * @group DDC-1372
13
+ */
14
+class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase
15
+{
16
+    static public function dataGetPlaceholderPositions()
17
+    {
18
+        return array(
19
+            // none
20
+            array('SELECT * FROM Foo', true, array()),
21
+            array('SELECT * FROM Foo', false, array()),
22
+
23
+            // Positionals
24
+            array('SELECT ?', true, array(7)),
25
+            array('SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, array(32, 35, 38)),
26
+            array('SELECT ? FROM ?', true, array(7, 14)),
27
+            array('SELECT "?" FROM foo', true, array()),
28
+            array("SELECT '?' FROM foo", true, array()),
29
+            array('SELECT "?" FROM foo WHERE bar = ?', true, array(32)),
30
+            array("SELECT '?' FROM foo WHERE bar = ?", true, array(32)),
31
+
32
+            // named
33
+            array('SELECT :foo FROM :bar', false, array(7 => 'foo', 17 => 'bar')),
34
+            array('SELECT * FROM Foo WHERE bar IN (:name1, :name2)', false, array(32 => 'name1', 40 => 'name2')),
35
+            array('SELECT ":foo" FROM Foo WHERE bar IN (:name1, :name2)', false, array(37 => 'name1', 45 => 'name2')),
36
+            array("SELECT ':foo' FROM Foo WHERE bar IN (:name1, :name2)", false, array(37 => 'name1', 45 => 'name2')),
37
+            array('SELECT :foo_id', false, array(7 => 'foo_id')), // Ticket DBAL-231
38
+            array('SELECT @rank := 1', false, array()), // Ticket DBAL-398
39
+            array('SELECT @rank := 1 AS rank, :foo AS foo FROM :bar', false, array(27 => 'foo', 44 => 'bar')), // Ticket DBAL-398
40
+        );
41
+    }
42
+
43
+    /**
44
+     * @dataProvider dataGetPlaceholderPositions
45
+     * @param type $query
46
+     * @param type $isPositional
47
+     * @param type $expectedParamPos
48
+     */
49
+    public function testGetPlaceholderPositions($query, $isPositional, $expectedParamPos)
50
+    {
51
+        $actualParamPos = SQLParserUtils::getPlaceholderPositions($query, $isPositional);
52
+        $this->assertEquals($expectedParamPos, $actualParamPos);
53
+    }
54
+
55
+    static public function dataExpandListParameters()
56
+    {
57
+        return array(
58
+            // Positional: Very simple with one needle
59
+            array(
60
+                "SELECT * FROM Foo WHERE foo IN (?)",
61
+                array(array(1, 2, 3)),
62
+                array(Connection::PARAM_INT_ARRAY),
63
+                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
64
+                array(1, 2, 3),
65
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
66
+            ),
67
+            // Positional: One non-list before d one after list-needle
68
+            array(
69
+                "SELECT * FROM Foo WHERE foo = ? AND bar IN (?)",
70
+                array("string", array(1, 2, 3)),
71
+                array(\PDO::PARAM_STR, Connection::PARAM_INT_ARRAY),
72
+                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
73
+                array("string", 1, 2, 3),
74
+                array(\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
75
+            ),
76
+            // Positional: One non-list after list-needle
77
+            array(
78
+                "SELECT * FROM Foo WHERE bar IN (?) AND baz = ?",
79
+                array(array(1, 2, 3), "foo"),
80
+                array(Connection::PARAM_INT_ARRAY, \PDO::PARAM_STR),
81
+                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
82
+                array(1, 2, 3, "foo"),
83
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
84
+            ),
85
+            // Positional: One non-list before and one after list-needle
86
+            array(
87
+                "SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?",
88
+                array(1, array(1, 2, 3), 4),
89
+                array(\PDO::PARAM_INT, Connection::PARAM_INT_ARRAY, \PDO::PARAM_INT),
90
+                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
91
+                array(1, 1, 2, 3, 4),
92
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
93
+            ),
94
+            // Positional: Two lists
95
+            array(
96
+                "SELECT * FROM Foo WHERE foo IN (?, ?)",
97
+                array(array(1, 2, 3), array(4, 5)),
98
+                array(Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY),
99
+                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
100
+                array(1, 2, 3, 4, 5),
101
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
102
+            ),
103
+            //  Positional : Empty "integer" array DDC-1978
104
+            array(
105
+                "SELECT * FROM Foo WHERE foo IN (?)",
106
+                array('foo'=>array()),
107
+                array('foo'=>Connection::PARAM_INT_ARRAY),
108
+                'SELECT * FROM Foo WHERE foo IN (?)',
109
+                array(),
110
+                array()
111
+            ),
112
+            //  Positional : Empty "str" array DDC-1978
113
+            array(
114
+                "SELECT * FROM Foo WHERE foo IN (?)",
115
+                array('foo'=>array()),
116
+                array('foo'=>Connection::PARAM_STR_ARRAY),
117
+                'SELECT * FROM Foo WHERE foo IN (?)',
118
+                array(),
119
+                array()
120
+            ),
121
+            //  Named parameters : Very simple with param int
122
+            array(
123
+                "SELECT * FROM Foo WHERE foo = :foo",
124
+                array('foo'=>1),
125
+                array('foo'=>\PDO::PARAM_INT),
126
+                'SELECT * FROM Foo WHERE foo = ?',
127
+                array(1),
128
+                array(\PDO::PARAM_INT)
129
+            ),
130
+
131
+             //  Named parameters : Very simple with param int and string
132
+            array(
133
+                "SELECT * FROM Foo WHERE foo = :foo AND bar = :bar",
134
+                array('bar'=>'Some String','foo'=>1),
135
+                array('foo'=>\PDO::PARAM_INT,'bar'=>\PDO::PARAM_STR),
136
+                'SELECT * FROM Foo WHERE foo = ? AND bar = ?',
137
+                array(1,'Some String'),
138
+                array(\PDO::PARAM_INT, \PDO::PARAM_STR)
139
+            ),
140
+
141
+            //  Named parameters : Very simple with one needle
142
+            array(
143
+                "SELECT * FROM Foo WHERE foo IN (:foo)",
144
+                array('foo'=>array(1, 2, 3)),
145
+                array('foo'=>Connection::PARAM_INT_ARRAY),
146
+                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
147
+                array(1, 2, 3),
148
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
149
+            ),
150
+            // Named parameters: One non-list before d one after list-needle
151
+            array(
152
+                "SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar)",
153
+                array('foo'=>"string", 'bar'=>array(1, 2, 3)),
154
+                array('foo'=>\PDO::PARAM_STR, 'bar'=>Connection::PARAM_INT_ARRAY),
155
+                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
156
+                array("string", 1, 2, 3),
157
+                array(\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
158
+            ),
159
+            // Named parameters: One non-list after list-needle
160
+            array(
161
+                "SELECT * FROM Foo WHERE bar IN (:bar) AND baz = :baz",
162
+                array('bar'=>array(1, 2, 3), 'baz'=>"foo"),
163
+                array('bar'=>Connection::PARAM_INT_ARRAY, 'baz'=>\PDO::PARAM_STR),
164
+                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
165
+                array(1, 2, 3, "foo"),
166
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
167
+            ),
168
+            // Named parameters: One non-list before and one after list-needle
169
+            array(
170
+                "SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar) AND baz = :baz",
171
+                array('bar'=>array(1, 2, 3),'foo'=>1, 'baz'=>4),
172
+                array('bar'=>Connection::PARAM_INT_ARRAY, 'foo'=>\PDO::PARAM_INT, 'baz'=>\PDO::PARAM_INT),
173
+                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
174
+                array(1, 1, 2, 3, 4),
175
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
176
+            ),
177
+            // Named parameters: Two lists
178
+            array(
179
+                "SELECT * FROM Foo WHERE foo IN (:a, :b)",
180
+                array('b'=>array(4, 5),'a'=>array(1, 2, 3)),
181
+                array('a'=>Connection::PARAM_INT_ARRAY, 'b'=>Connection::PARAM_INT_ARRAY),
182
+                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
183
+                array(1, 2, 3, 4, 5),
184
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
185
+            ),
186
+            //  Named parameters : With the same name arg type string
187
+            array(
188
+                "SELECT * FROM Foo WHERE foo <> :arg AND bar = :arg",
189
+                array('arg'=>"Some String"),
190
+                array('arg'=>\PDO::PARAM_STR),
191
+                'SELECT * FROM Foo WHERE foo <> ? AND bar = ?',
192
+                array("Some String","Some String"),
193
+                array(\PDO::PARAM_STR,\PDO::PARAM_STR,)
194
+            ),
195
+             //  Named parameters : With the same name arg
196
+            array(
197
+                "SELECT * FROM Foo WHERE foo IN (:arg) AND NOT bar IN (:arg)",
198
+                array('arg'=>array(1, 2, 3)),
199
+                array('arg'=>Connection::PARAM_INT_ARRAY),
200
+                'SELECT * FROM Foo WHERE foo IN (?, ?, ?) AND NOT bar IN (?, ?, ?)',
201
+                array(1, 2, 3, 1, 2, 3),
202
+                array(\PDO::PARAM_INT,\PDO::PARAM_INT, \PDO::PARAM_INT,\PDO::PARAM_INT,\PDO::PARAM_INT, \PDO::PARAM_INT)
203
+            ),
204
+
205
+             //  Named parameters : Same name, other name in between DBAL-299
206
+            array(
207
+                "SELECT * FROM Foo WHERE (:foo = 2) AND (:bar = 3) AND (:foo = 2)",
208
+                array('foo'=>2,'bar'=>3),
209
+                array('foo'=>\PDO::PARAM_INT,'bar'=>\PDO::PARAM_INT),
210
+                'SELECT * FROM Foo WHERE (? = 2) AND (? = 3) AND (? = 2)',
211
+                array(2, 3, 2),
212
+                array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
213
+            ),
214
+             //  Named parameters : Empty "integer" array DDC-1978
215
+            array(
216
+                "SELECT * FROM Foo WHERE foo IN (:foo)",
217
+                array('foo'=>array()),
218
+                array('foo'=>Connection::PARAM_INT_ARRAY),
219
+                'SELECT * FROM Foo WHERE foo IN (?)',
220
+                array(),
221
+                array()
222
+            ),
223
+             //  Named parameters : Two empty "str" array DDC-1978
224
+            array(
225
+                "SELECT * FROM Foo WHERE foo IN (:foo) OR bar IN (:bar)",
226
+                array('foo'=>array(), 'bar'=>array()),
227
+                array('foo'=>Connection::PARAM_STR_ARRAY, 'bar'=>Connection::PARAM_STR_ARRAY),
228
+                'SELECT * FROM Foo WHERE foo IN (?) OR bar IN (?)',
229
+                array(),
230
+                array()
231
+            ),
232
+        );
233
+    }
234
+
235
+    /**
236
+     * @dataProvider dataExpandListParameters
237
+     * @param type $q
238
+     * @param type $p
239
+     * @param type $t
240
+     * @param type $expectedQuery
241
+     * @param type $expectedParams
242
+     * @param type $expectedTypes
243
+     */
244
+    public function testExpandListParameters($q, $p, $t, $expectedQuery, $expectedParams, $expectedTypes)
245
+    {
246
+        list($query, $params, $types) = SQLParserUtils::expandListParameters($q, $p, $t);
247
+
248
+        $this->assertEquals($expectedQuery, $query, "Query was not rewritten correctly.");
249
+        $this->assertEquals($expectedParams, $params, "Params dont match");
250
+        $this->assertEquals($expectedTypes, $types, "Types dont match");
251
+    }
252
+}

+ 8 - 0
vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/.travis.yml 查看文件

@@ -0,0 +1,8 @@
1
+language: php
2
+
3
+php:
4
+  - 5.3
5
+  - 5.4
6
+
7
+before_script: composer install --dev
8
+

+ 32 - 0
vendor/doctrine/orm/composer.json 查看文件

@@ -0,0 +1,32 @@
1
+{
2
+    "name": "doctrine/orm",
3
+    "type": "library","version":"2.3.2",
4
+    "description": "Object-Relational-Mapper for PHP",
5
+    "keywords": ["orm", "database"],
6
+    "homepage": "http://www.doctrine-project.org",
7
+    "license": "MIT",
8
+    "authors": [
9
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
10
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
11
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
12
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
13
+    ],
14
+    "require": {
15
+        "php": ">=5.3.2",
16
+        "ext-pdo": "*",
17
+        "doctrine/dbal": "2.3.*",
18
+        "symfony/console": "2.*"
19
+    },
20
+    "suggest": {
21
+        "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
22
+    },
23
+    "autoload": {
24
+        "psr-0": { "Doctrine\\ORM": "lib/" }
25
+    },
26
+    "bin": ["bin/doctrine", "bin/doctrine.php"],
27
+    "extra": {
28
+        "branch-alias": {
29
+            "dev-master": "2.3.x-dev"
30
+        }
31
+    }
32
+}

+ 59 - 0
vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php 查看文件

@@ -0,0 +1,59 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+namespace Doctrine\ORM\Id;
21
+
22
+use Doctrine\ORM\EntityManager;
23
+
24
+/**
25
+ * Id generator that obtains IDs from special "identity" columns. These are columns
26
+ * that automatically get a database-generated, auto-incremented identifier on INSERT.
27
+ * This generator obtains the last insert id after such an insert.
28
+ */
29
+class IdentityGenerator extends AbstractIdGenerator
30
+{
31
+    /** @var string The name of the sequence to pass to lastInsertId(), if any. */
32
+    private $_seqName;
33
+
34
+    /**
35
+     * @param string $seqName The name of the sequence to pass to lastInsertId()
36
+     *                        to obtain the last generated identifier within the current
37
+     *                        database session/connection, if any.
38
+     */
39
+    public function __construct($seqName = null)
40
+    {
41
+        $this->_seqName = $seqName;
42
+    }
43
+
44
+    /**
45
+     * {@inheritdoc}
46
+     */
47
+    public function generate(EntityManager $em, $entity)
48
+    {
49
+        return (int)$em->getConnection()->lastInsertId($this->_seqName);
50
+    }
51
+
52
+    /**
53
+     * {@inheritdoc}
54
+     */
55
+    public function isPostInsertGenerator()
56
+    {
57
+        return true;
58
+    }
59
+}

+ 537 - 0
vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php 查看文件

@@ -0,0 +1,537 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+namespace Doctrine\ORM\Mapping;
21
+
22
+use ReflectionException,
23
+    Doctrine\ORM\ORMException,
24
+    Doctrine\ORM\EntityManager,
25
+    Doctrine\DBAL\Platforms,
26
+    Doctrine\ORM\Events,
27
+    Doctrine\Common\Persistence\Mapping\ReflectionService,
28
+    Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface,
29
+    Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory,
30
+    Doctrine\ORM\Id\IdentityGenerator,
31
+    Doctrine\ORM\Event\LoadClassMetadataEventArgs;
32
+
33
+/**
34
+ * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
35
+ * metadata mapping information of a class which describes how a class should be mapped
36
+ * to a relational database.
37
+ *
38
+ * @since   2.0
39
+ * @author  Benjamin Eberlei <kontakt@beberlei.de>
40
+ * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
41
+ * @author  Jonathan Wage <jonwage@gmail.com>
42
+ * @author  Roman Borschel <roman@code-factory.org>
43
+ */
44
+class ClassMetadataFactory extends AbstractClassMetadataFactory
45
+{
46
+    /**
47
+     * @var EntityManager
48
+     */
49
+    private $em;
50
+
51
+    /**
52
+     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
53
+     */
54
+    private $targetPlatform;
55
+
56
+    /**
57
+     * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
58
+     */
59
+    private $driver;
60
+
61
+    /**
62
+     * @var \Doctrine\Common\EventManager
63
+     */
64
+    private $evm;
65
+
66
+    /**
67
+     * @param EntityManager $em
68
+     */
69
+    public function setEntityManager(EntityManager $em)
70
+    {
71
+        $this->em = $em;
72
+    }
73
+
74
+    /**
75
+     * {@inheritDoc}.
76
+     */
77
+    protected function initialize()
78
+    {
79
+        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
80
+        $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
81
+        $this->evm = $this->em->getEventManager();
82
+        $this->initialized = true;
83
+    }
84
+
85
+    /**
86
+     * {@inheritDoc}
87
+     */
88
+    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
89
+    {
90
+        /* @var $class ClassMetadata */
91
+        /* @var $parent ClassMetadata */
92
+        if ($parent) {
93
+            $class->setInheritanceType($parent->inheritanceType);
94
+            $class->setDiscriminatorColumn($parent->discriminatorColumn);
95
+            $class->setIdGeneratorType($parent->generatorType);
96
+            $this->addInheritedFields($class, $parent);
97
+            $this->addInheritedRelations($class, $parent);
98
+            $class->setIdentifier($parent->identifier);
99
+            $class->setVersioned($parent->isVersioned);
100
+            $class->setVersionField($parent->versionField);
101
+            $class->setDiscriminatorMap($parent->discriminatorMap);
102
+            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
103
+            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
104
+
105
+            if ($parent->isMappedSuperclass) {
106
+                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
107
+            }
108
+        }
109
+
110
+        // Invoke driver
111
+        try {
112
+            $this->driver->loadMetadataForClass($class->getName(), $class);
113
+        } catch (ReflectionException $e) {
114
+            throw MappingException::reflectionFailure($class->getName(), $e);
115
+        }
116
+
117
+        // If this class has a parent the id generator strategy is inherited.
118
+        // However this is only true if the hierarchy of parents contains the root entity,
119
+        // if it consists of mapped superclasses these don't necessarily include the id field.
120
+        if ($parent && $rootEntityFound) {
121
+            if ($parent->isIdGeneratorSequence()) {
122
+                $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
123
+            } else if ($parent->isIdGeneratorTable()) {
124
+                $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition;
125
+            }
126
+
127
+            if ($parent->generatorType) {
128
+                $class->setIdGeneratorType($parent->generatorType);
129
+            }
130
+
131
+            if ($parent->idGenerator) {
132
+                $class->setIdGenerator($parent->idGenerator);
133
+            }
134
+        } else {
135
+            $this->completeIdGeneratorMapping($class);
136
+        }
137
+
138
+        if ($parent && $parent->isInheritanceTypeSingleTable()) {
139
+            $class->setPrimaryTable($parent->table);
140
+        }
141
+
142
+        if ($parent && $parent->containsForeignIdentifier) {
143
+            $class->containsForeignIdentifier = true;
144
+        }
145
+
146
+        if ($parent && !empty($parent->namedQueries)) {
147
+            $this->addInheritedNamedQueries($class, $parent);
148
+        }
149
+
150
+        if ($parent && !empty($parent->namedNativeQueries)) {
151
+            $this->addInheritedNamedNativeQueries($class, $parent);
152
+        }
153
+
154
+        if ($parent && !empty($parent->sqlResultSetMappings)) {
155
+            $this->addInheritedSqlResultSetMappings($class, $parent);
156
+        }
157
+
158
+        $class->setParentClasses($nonSuperclassParents);
159
+
160
+        if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
161
+            $this->addDefaultDiscriminatorMap($class);
162
+        }
163
+
164
+        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
165
+            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
166
+            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
167
+        }
168
+
169
+        $this->wakeupReflection($class, $this->getReflectionService());
170
+        $this->validateRuntimeMetadata($class, $parent);
171
+    }
172
+
173
+    /**
174
+     * Validate runtime metadata is correctly defined.
175
+     *
176
+     * @param ClassMetadata $class
177
+     * @param $parent
178
+     * @throws MappingException
179
+     */
180
+    protected function validateRuntimeMetadata($class, $parent)
181
+    {
182
+        if ( ! $class->reflClass ) {
183
+            // only validate if there is a reflection class instance
184
+            return;
185
+        }
186
+
187
+        $class->validateIdentifier();
188
+        $class->validateAssocations();
189
+        $class->validateLifecycleCallbacks($this->getReflectionService());
190
+
191
+        // verify inheritance
192
+        if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
193
+            if ( ! $parent) {
194
+                if (count($class->discriminatorMap) == 0) {
195
+                    throw MappingException::missingDiscriminatorMap($class->name);
196
+                }
197
+                if ( ! $class->discriminatorColumn) {
198
+                    throw MappingException::missingDiscriminatorColumn($class->name);
199
+                }
200
+            } else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
201
+                // enforce discriminator map for all entities of an inheritance hierarchy, otherwise problems will occur.
202
+                throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
203
+            }
204
+        } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
205
+            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
206
+            throw MappingException::noInheritanceOnMappedSuperClass($class->name);
207
+        }
208
+    }
209
+
210
+    /**
211
+     * {@inheritDoc}
212
+     */
213
+    protected function newClassMetadataInstance($className)
214
+    {
215
+        return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
216
+    }
217
+
218
+    /**
219
+     * Adds a default discriminator map if no one is given
220
+     *
221
+     * If an entity is of any inheritance type and does not contain a
222
+     * discriminator map, then the map is generated automatically. This process
223
+     * is expensive computation wise.
224
+     *
225
+     * The automatically generated discriminator map contains the lowercase short name of
226
+     * each class as key.
227
+     *
228
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
229
+     * @throws MappingException
230
+     */
231
+    private function addDefaultDiscriminatorMap(ClassMetadata $class)
232
+    {
233
+        $allClasses = $this->driver->getAllClassNames();
234
+        $fqcn = $class->getName();
235
+        $map = array($this->getShortName($class->name) => $fqcn);
236
+
237
+        $duplicates = array();
238
+        foreach ($allClasses as $subClassCandidate) {
239
+            if (is_subclass_of($subClassCandidate, $fqcn)) {
240
+                $shortName = $this->getShortName($subClassCandidate);
241
+
242
+                if (isset($map[$shortName])) {
243
+                    $duplicates[] = $shortName;
244
+                }
245
+
246
+                $map[$shortName] = $subClassCandidate;
247
+            }
248
+        }
249
+
250
+        if ($duplicates) {
251
+            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
252
+        }
253
+
254
+        $class->setDiscriminatorMap($map);
255
+    }
256
+
257
+    /**
258
+     * Get the lower-case short name of a class.
259
+     *
260
+     * @param string $className
261
+     * @return string
262
+     */
263
+    private function getShortName($className)
264
+    {
265
+        if (strpos($className, "\\") === false) {
266
+            return strtolower($className);
267
+        }
268
+
269
+        $parts = explode("\\", $className);
270
+        return strtolower(end($parts));
271
+    }
272
+
273
+    /**
274
+     * Adds inherited fields to the subclass mapping.
275
+     *
276
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
277
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
278
+     */
279
+    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
280
+    {
281
+        foreach ($parentClass->fieldMappings as $mapping) {
282
+            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
283
+                $mapping['inherited'] = $parentClass->name;
284
+            }
285
+            if ( ! isset($mapping['declared'])) {
286
+                $mapping['declared'] = $parentClass->name;
287
+            }
288
+            $subClass->addInheritedFieldMapping($mapping);
289
+        }
290
+        foreach ($parentClass->reflFields as $name => $field) {
291
+            $subClass->reflFields[$name] = $field;
292
+        }
293
+    }
294
+
295
+    /**
296
+     * Adds inherited association mappings to the subclass mapping.
297
+     *
298
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
299
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
300
+     * @throws MappingException
301
+     */
302
+    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
303
+    {
304
+        foreach ($parentClass->associationMappings as $field => $mapping) {
305
+            if ($parentClass->isMappedSuperclass) {
306
+                if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
307
+                    throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field);
308
+                }
309
+                $mapping['sourceEntity'] = $subClass->name;
310
+            }
311
+
312
+            //$subclassMapping = $mapping;
313
+            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
314
+                $mapping['inherited'] = $parentClass->name;
315
+            }
316
+            if ( ! isset($mapping['declared'])) {
317
+                $mapping['declared'] = $parentClass->name;
318
+            }
319
+            $subClass->addInheritedAssociationMapping($mapping);
320
+        }
321
+    }
322
+
323
+    /**
324
+     * Adds inherited named queries to the subclass mapping.
325
+     *
326
+     * @since 2.2
327
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
328
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
329
+     */
330
+    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
331
+    {
332
+        foreach ($parentClass->namedQueries as $name => $query) {
333
+            if ( ! isset ($subClass->namedQueries[$name])) {
334
+                $subClass->addNamedQuery(array(
335
+                    'name'  => $query['name'],
336
+                    'query' => $query['query']
337
+                ));
338
+            }
339
+        }
340
+    }
341
+
342
+    /**
343
+     * Adds inherited named native queries to the subclass mapping.
344
+     *
345
+     * @since 2.3
346
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
347
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
348
+     */
349
+    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
350
+    {
351
+        foreach ($parentClass->namedNativeQueries as $name => $query) {
352
+            if ( ! isset ($subClass->namedNativeQueries[$name])) {
353
+                $subClass->addNamedNativeQuery(array(
354
+                    'name'              => $query['name'],
355
+                    'query'             => $query['query'],
356
+                    'isSelfClass'       => $query['isSelfClass'],
357
+                    'resultSetMapping'  => $query['resultSetMapping'],
358
+                    'resultClass'       => $query['isSelfClass'] ? $subClass->name : $query['resultClass'],
359
+                ));
360
+            }
361
+        }
362
+    }
363
+
364
+    /**
365
+     * Adds inherited sql result set mappings to the subclass mapping.
366
+     *
367
+     * @since 2.3
368
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
369
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
370
+     */
371
+    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
372
+    {
373
+        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
374
+            if ( ! isset ($subClass->sqlResultSetMappings[$name])) {
375
+                $entities = array();
376
+                foreach ($mapping['entities'] as $entity) {
377
+                    $entities[] = array(
378
+                        'fields'                => $entity['fields'],
379
+                        'isSelfClass'           => $entity['isSelfClass'],
380
+                        'discriminatorColumn'   => $entity['discriminatorColumn'],
381
+                        'entityClass'           => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'],
382
+                    );
383
+                }
384
+
385
+                $subClass->addSqlResultSetMapping(array(
386
+                    'name'          => $mapping['name'],
387
+                    'columns'       => $mapping['columns'],
388
+                    'entities'      => $entities,
389
+                ));
390
+            }
391
+        }
392
+    }
393
+
394
+    /**
395
+     * Completes the ID generator mapping. If "auto" is specified we choose the generator
396
+     * most appropriate for the targeted database platform.
397
+     *
398
+     * @param ClassMetadataInfo $class
399
+     * @throws ORMException
400
+     */
401
+    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
402
+    {
403
+        $idGenType = $class->generatorType;
404
+        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
405
+            if ($this->targetPlatform->prefersSequences()) {
406
+                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
407
+            } else if ($this->targetPlatform->prefersIdentityColumns()) {
408
+                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
409
+            } else {
410
+                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
411
+            }
412
+        }
413
+
414
+        // Create & assign an appropriate ID generator instance
415
+        switch ($class->generatorType) {
416
+            case ClassMetadata::GENERATOR_TYPE_IDENTITY:
417
+                // For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
418
+                // <table>_<column>_seq in PostgreSQL for SERIAL columns.
419
+                // Not pretty but necessary and the simplest solution that currently works.
420
+                $sequenceName = null;
421
+
422
+                if ($this->targetPlatform instanceof Platforms\PostgreSQLPlatform) {
423
+                    $fieldName      = $class->getSingleIdentifierFieldName();
424
+                    $columnName     = $class->getSingleIdentifierColumnName();
425
+                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
426
+                    $sequenceName   = $class->getTableName() . '_' . $columnName . '_seq';
427
+                    $definition     = array(
428
+                        'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName)
429
+                    );
430
+
431
+                    if ($quoted) {
432
+                        $definition['quoted'] = true;
433
+                    }
434
+
435
+                    $sequenceName = $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform);
436
+                }
437
+
438
+                $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($sequenceName));
439
+                break;
440
+
441
+            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
442
+                // If there is no sequence definition yet, create a default definition
443
+                $definition = $class->sequenceGeneratorDefinition;
444
+
445
+                if ( ! $definition) {
446
+                    $fieldName      = $class->getSingleIdentifierFieldName();
447
+                    $columnName     = $class->getSingleIdentifierColumnName();
448
+                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
449
+                    $sequenceName   = $class->getTableName() . '_' . $columnName . '_seq';
450
+                    $definition     = array(
451
+                        'sequenceName'      => $this->targetPlatform->fixSchemaElementName($sequenceName),
452
+                        'allocationSize'    => 1,
453
+                        'initialValue'      => 1,
454
+                    );
455
+
456
+                    if ($quoted) {
457
+                        $definition['quoted'] = true;
458
+                    }
459
+
460
+                    $class->setSequenceGeneratorDefinition($definition);
461
+                }
462
+
463
+                $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
464
+                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform),
465
+                    $definition['allocationSize']
466
+                );
467
+                $class->setIdGenerator($sequenceGenerator);
468
+                break;
469
+
470
+            case ClassMetadata::GENERATOR_TYPE_NONE:
471
+                $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
472
+                break;
473
+
474
+            case ClassMetadata::GENERATOR_TYPE_UUID:
475
+                $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator());
476
+                break;
477
+
478
+            case ClassMetadata::GENERATOR_TYPE_TABLE:
479
+                throw new ORMException("TableGenerator not yet implemented.");
480
+                break;
481
+
482
+            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
483
+                $definition = $class->customGeneratorDefinition;
484
+                if ( ! class_exists($definition['class'])) {
485
+                    throw new ORMException("Can't instantiate custom generator : " .
486
+                        $definition['class']);
487
+                }
488
+                $class->setIdGenerator(new $definition['class']);
489
+                break;
490
+
491
+            default:
492
+                throw new ORMException("Unknown generator type: " . $class->generatorType);
493
+        }
494
+    }
495
+
496
+    /**
497
+     * {@inheritDoc}
498
+     */
499
+    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
500
+    {
501
+        /* @var $class ClassMetadata */
502
+        $class->wakeupReflection($reflService);
503
+    }
504
+
505
+    /**
506
+     * {@inheritDoc}
507
+     */
508
+    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
509
+    {
510
+        /* @var $class ClassMetadata */
511
+        $class->initializeReflection($reflService);
512
+    }
513
+
514
+    /**
515
+     * {@inheritDoc}
516
+     */
517
+    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
518
+    {
519
+        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
520
+    }
521
+
522
+    /**
523
+     * {@inheritDoc}
524
+     */
525
+    protected function getDriver()
526
+    {
527
+        return $this->driver;
528
+    }
529
+
530
+    /**
531
+     * {@inheritDoc}
532
+     */
533
+    protected function isEntity(ClassMetadataInterface $class)
534
+    {
535
+        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
536
+    }
537
+}

文件差異過大導致無法顯示
+ 3050 - 0
vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php


+ 55 - 0
vendor/doctrine/orm/lib/Doctrine/ORM/Version.php 查看文件

@@ -0,0 +1,55 @@
1
+<?php
2
+/*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+namespace Doctrine\ORM;
21
+
22
+/**
23
+ * Class to store and retrieve the version of Doctrine
24
+ *
25
+ * 
26
+ * @link    www.doctrine-project.org
27
+ * @since   2.0
28
+ * @version $Revision$
29
+ * @author  Benjamin Eberlei <kontakt@beberlei.de>
30
+ * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
31
+ * @author  Jonathan Wage <jonwage@gmail.com>
32
+ * @author  Roman Borschel <roman@code-factory.org>
33
+ */
34
+class Version
35
+{
36
+    /**
37
+     * Current Doctrine Version
38
+     */
39
+    const VERSION = '2.3.2';
40
+
41
+    /**
42
+     * Compares a Doctrine version with the current one.
43
+     *
44
+     * @param string $version Doctrine version to compare.
45
+     * @return int Returns -1 if older, 0 if it is the same, 1 if version
46
+     *             passed as argument is newer.
47
+     */
48
+    public static function compare($version)
49
+    {
50
+        $currentVersion = str_replace(' ', '', strtolower(self::VERSION));
51
+        $version        = str_replace(' ', '', $version);
52
+
53
+        return version_compare($version, $currentVersion);
54
+    }
55
+}

+ 464 - 0
vendor/doctrine/orm/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php 查看文件

@@ -0,0 +1,464 @@
1
+<?php
2
+
3
+namespace Doctrine\Tests\ORM\Functional\Ticket;
4
+
5
+use Doctrine\Tests\Models\DDC117\DDC117ArticleDetails;
6
+use Doctrine\Tests\Models\DDC117\DDC117Article;
7
+use Doctrine\Tests\Models\DDC117\DDC117Reference;
8
+use Doctrine\Tests\Models\DDC117\DDC117Translation;
9
+use Doctrine\Tests\Models\DDC117\DDC117ApproveChanges;
10
+use Doctrine\Tests\Models\DDC117\DDC117Editor;
11
+
12
+require_once __DIR__ . '/../../../TestInit.php';
13
+
14
+class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
15
+{
16
+    private $article1;
17
+    private $article2;
18
+    private $reference;
19
+    private $translation;
20
+    private $articleDetails;
21
+
22
+    protected function setUp() {
23
+        $this->useModelSet('ddc117');
24
+        parent::setUp();
25
+
26
+        $this->article1 = new DDC117Article("Foo");
27
+        $this->article2 = new DDC117Article("Bar");
28
+
29
+        $this->_em->persist($this->article1);
30
+        $this->_em->persist($this->article2);
31
+        $this->_em->flush();
32
+
33
+        $this->reference = new DDC117Reference($this->article1, $this->article2, "Test-Description");
34
+        $this->_em->persist($this->reference);
35
+
36
+        $this->translation = new DDC117Translation($this->article1, "en", "Bar");
37
+        $this->_em->persist($this->translation);
38
+
39
+        $this->articleDetails = new DDC117ArticleDetails($this->article1, "Very long text");
40
+        $this->_em->persist($this->articleDetails);
41
+        $this->_em->flush();
42
+
43
+        $this->_em->clear();
44
+    }
45
+
46
+    /**
47
+     * @group DDC-117
48
+     */
49
+    public function testAssociationOnlyCompositeKey()
50
+    {
51
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
52
+
53
+        $mapRef = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
54
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $mapRef);
55
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $mapRef->target());
56
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $mapRef->source());
57
+        $this->assertSame($mapRef, $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria));
58
+
59
+        $this->_em->clear();
60
+
61
+        $dql = "SELECT r, s FROM "."Doctrine\Tests\Models\DDC117\DDC117Reference r JOIN r.source s WHERE r.source = ?1";
62
+        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 1)->getSingleResult();
63
+
64
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $mapRef);
65
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $mapRef->target());
66
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $mapRef->source());
67
+        $this->assertSame($dqlRef, $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria));
68
+
69
+        $this->_em->clear();
70
+
71
+        $dql = "SELECT r, s FROM "."Doctrine\Tests\Models\DDC117\DDC117Reference r JOIN r.source s WHERE s.title = ?1";
72
+        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getSingleResult();
73
+
74
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $dqlRef);
75
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $dqlRef->target());
76
+        $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Article", $dqlRef->source());
77
+        $this->assertSame($dqlRef, $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria));
78
+
79
+        $dql = "SELECT r, s FROM "."Doctrine\Tests\Models\DDC117\DDC117Reference r JOIN r.source s WHERE s.title = ?1";
80
+        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getSingleResult();
81
+
82
+        $this->_em->contains($dqlRef);
83
+    }
84
+
85
+    /**
86
+     * @group DDC-117
87
+     */
88
+    public function testUpdateAssocationEntity()
89
+    {
90
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
91
+
92
+        $mapRef = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
93
+        $this->assertNotNull($mapRef);
94
+        $mapRef->setDescription("New Description!!");
95
+        $this->_em->flush();
96
+        $this->_em->clear();
97
+
98
+        $mapRef = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
99
+
100
+        $this->assertEquals('New Description!!', $mapRef->getDescription());
101
+    }
102
+
103
+    /**
104
+     * @group DDC-117
105
+     */
106
+    public function testFetchDql()
107
+    {
108
+        $dql = "SELECT r, s FROM "."Doctrine\Tests\Models\DDC117\DDC117Reference r JOIN r.source s WHERE s.title = ?1";
109
+        $refs = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getResult();
110
+
111
+        $this->assertTrue(count($refs) > 0, "Has to contain at least one Reference.");
112
+
113
+        foreach ($refs AS $ref) {
114
+            $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $ref, "Contains only Reference instances.");
115
+            $this->assertTrue($this->_em->contains($ref), "Contains Reference in the IdentityMap.");
116
+        }
117
+    }
118
+
119
+    /**
120
+     * @group DDC-117
121
+     */
122
+    public function testRemoveCompositeElement()
123
+    {
124
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
125
+
126
+        $refRep = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
127
+
128
+        $this->_em->remove($refRep);
129
+        $this->_em->flush();
130
+        $this->_em->clear();
131
+
132
+        $this->assertNull($this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria));
133
+    }
134
+
135
+    /**
136
+     * @group DDC-117
137
+     */
138
+    public function testDqlRemoveCompositeElement()
139
+    {
140
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
141
+
142
+        $dql = "DELETE "."Doctrine\Tests\Models\DDC117\DDC117Reference r WHERE r.source = ?1 AND r.target = ?2";
143
+        $this->_em->createQuery($dql)
144
+                  ->setParameter(1, $this->article1->id())
145
+                  ->setParameter(2, $this->article2->id())
146
+                  ->execute();
147
+
148
+        $this->assertNull($this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria));
149
+    }
150
+
151
+    /**
152
+     * @group DDC-117
153
+     */
154
+    public function testInverseSideAccess()
155
+    {
156
+        $this->article1 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id());
157
+
158
+        $this->assertEquals(1, count($this->article1->references()));
159
+
160
+        foreach ($this->article1->references() AS $this->reference) {
161
+            $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $this->reference);
162
+            $this->assertSame($this->article1, $this->reference->source());
163
+        }
164
+
165
+        $this->_em->clear();
166
+
167
+        $dql = 'SELECT a, r FROM '. 'Doctrine\Tests\Models\DDC117\DDC117Article a INNER JOIN a.references r WHERE a.id = ?1';
168
+        $articleDql = $this->_em->createQuery($dql)
169
+                                ->setParameter(1, $this->article1->id())
170
+                                ->getSingleResult();
171
+
172
+        $this->assertEquals(1, count($this->article1->references()));
173
+
174
+        foreach ($this->article1->references() AS $this->reference) {
175
+            $this->assertInstanceOf("Doctrine\Tests\Models\DDC117\DDC117Reference", $this->reference);
176
+            $this->assertSame($this->article1, $this->reference->source());
177
+        }
178
+    }
179
+
180
+    /**
181
+     * @group DDC-117
182
+     */
183
+    public function testMixedCompositeKey()
184
+    {
185
+        $idCriteria = array('article' => $this->article1->id(), 'language' => 'en');
186
+
187
+        $this->translation = $this->_em->find('Doctrine\Tests\Models\DDC117\DDC117Translation', $idCriteria);
188
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Translation', $this->translation);
189
+
190
+        $this->assertSame($this->translation, $this->_em->find('Doctrine\Tests\Models\DDC117\DDC117Translation', $idCriteria));
191
+
192
+        $this->_em->clear();
193
+
194
+        $dql = 'SELECT t, a FROM ' . 'Doctrine\Tests\Models\DDC117\DDC117Translation t JOIN t.article a WHERE t.article = ?1 AND t.language = ?2';
195
+        $dqlTrans = $this->_em->createQuery($dql)
196
+                              ->setParameter(1, $this->article1->id())
197
+                              ->setParameter(2, 'en')
198
+                              ->getSingleResult();
199
+
200
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Translation', $this->translation);
201
+    }
202
+
203
+    /**
204
+     * @group DDC-117
205
+     */
206
+    public function testMixedCompositeKeyViolateUniqueness()
207
+    {
208
+        $this->article1 = $this->_em->find('Doctrine\Tests\Models\DDC117\DDC117Article', $this->article1->id());
209
+        $this->article1->addTranslation('en', 'Bar');
210
+        $this->article1->addTranslation('en', 'Baz');
211
+
212
+        $exceptionThrown = false;
213
+        try {
214
+            // exception depending on the underyling Database Driver
215
+            $this->_em->flush();
216
+        } catch(\Exception $e) {
217
+            $exceptionThrown = true;
218
+        }
219
+
220
+        $this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
221
+    }
222
+
223
+    /**
224
+     * @group DDC-117
225
+     */
226
+    public function testOneToOneForeignObjectId()
227
+    {
228
+        $this->article1 = new DDC117Article("Foo");
229
+        $this->_em->persist($this->article1);
230
+        $this->_em->flush();
231
+
232
+        $this->articleDetails = new DDC117ArticleDetails($this->article1, "Very long text");
233
+        $this->_em->persist($this->articleDetails);
234
+        $this->_em->flush();
235
+
236
+        $this->articleDetails->update("not so very long text!");
237
+        $this->_em->flush();
238
+        $this->_em->clear();
239
+
240
+        /* @var $article DDC117Article */
241
+        $article = $this->_em->find(get_class($this->article1), $this->article1->id());
242
+        $this->assertEquals('not so very long text!', $article->getText());
243
+    }
244
+
245
+    /**
246
+     * @group DDC-117
247
+     */
248
+    public function testOneToOneCascadeRemove()
249
+    {
250
+        $article = $this->_em->find(get_class($this->article1), $this->article1->id());
251
+        $this->_em->remove($article);
252
+        $this->_em->flush();
253
+
254
+        $this->assertFalse($this->_em->contains($article->getDetails()));
255
+    }
256
+
257
+    /**
258
+     * @group DDC-117
259
+     */
260
+    public function testOneToOneCascadePersist()
261
+    {
262
+        if (!$this->_em->getConnection()->getDatabasePlatform()->prefersSequences()) {
263
+            $this->markTestSkipped('Test only works with databases that prefer sequences as ID strategy.');
264
+        }
265
+
266
+        $this->article1 = new DDC117Article("Foo");
267
+
268
+        $this->articleDetails = new DDC117ArticleDetails($this->article1, "Very long text");
269
+
270
+        $this->_em->persist($this->article1);
271
+        $this->_em->flush();
272
+    }
273
+
274
+    /**
275
+     * @group DDC-117
276
+     */
277
+    public function testReferencesToForeignKeyEntities()
278
+    {
279
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
280
+        $reference = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
281
+
282
+        $idCriteria = array('article' => $this->article1->id(), 'language' => 'en');
283
+        $translation = $this->_em->find('Doctrine\Tests\Models\DDC117\DDC117Translation', $idCriteria);
284
+
285
+        $approveChanges = new DDC117ApproveChanges($reference->source()->getDetails(), $reference, $translation);
286
+        $this->_em->persist($approveChanges);
287
+        $this->_em->flush();
288
+        $this->_em->clear();
289
+
290
+        $approveChanges = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117ApproveChanges", $approveChanges->getId());
291
+
292
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails', $approveChanges->getArticleDetails());
293
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Reference', $approveChanges->getReference());
294
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Translation', $approveChanges->getTranslation());
295
+    }
296
+
297
+    /**
298
+     * @group DDC-117
299
+     */
300
+    public function testLoadOneToManyCollectionOfForeignKeyEntities()
301
+    {
302
+        /* @var $article DDC117Article */
303
+        $article = $this->_em->find(get_class($this->article1), $this->article1->id());
304
+
305
+        $translations = $article->getTranslations();
306
+        $this->assertFalse($translations->isInitialized());
307
+        $this->assertContainsOnly('Doctrine\Tests\Models\DDC117\DDC117Translation', $translations);
308
+        $this->assertTrue($translations->isInitialized());
309
+    }
310
+
311
+    /**
312
+     * @group DDC-117
313
+     */
314
+    public function testLoadManyToManyCollectionOfForeignKeyEntities()
315
+    {
316
+        $editor = $this->loadEditorFixture();
317
+
318
+        $this->assertFalse($editor->reviewingTranslations->isInitialized());
319
+        $this->assertContainsOnly("Doctrine\Tests\Models\DDC117\DDC117Translation", $editor->reviewingTranslations);
320
+        $this->assertTrue($editor->reviewingTranslations->isInitialized());
321
+
322
+        $this->_em->clear();
323
+
324
+        $dql = "SELECT e, t FROM Doctrine\Tests\Models\DDC117\DDC117Editor e JOIN e.reviewingTranslations t WHERE e.id = ?1";
325
+        $editor = $this->_em->createQuery($dql)->setParameter(1, $editor->id)->getSingleResult();
326
+        $this->assertTrue($editor->reviewingTranslations->isInitialized());
327
+        $this->assertContainsOnly("Doctrine\Tests\Models\DDC117\DDC117Translation", $editor->reviewingTranslations);
328
+    }
329
+
330
+    /**
331
+     * @group DDC-117
332
+     */
333
+    public function testClearManyToManyCollectionOfForeignKeyEntities()
334
+    {
335
+        $editor = $this->loadEditorFixture();
336
+        $this->assertEquals(3, count($editor->reviewingTranslations));
337
+
338
+        $editor->reviewingTranslations->clear();
339
+        $this->_em->flush();
340
+        $this->_em->clear();
341
+
342
+        $editor = $this->_em->find(get_class($editor), $editor->id);
343
+        $this->assertEquals(0, count($editor->reviewingTranslations));
344
+    }
345
+
346
+    /**
347
+     * @group DDC-117
348
+     */
349
+    public function testLoadInverseManyToManyCollection()
350
+    {
351
+        $editor = $this->loadEditorFixture();
352
+
353
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Translation', $editor->reviewingTranslations[0]);
354
+
355
+        $reviewedBy = $editor->reviewingTranslations[0]->getReviewedByEditors();
356
+        $this->assertEquals(1, count($reviewedBy));
357
+        $this->assertSame($editor, $reviewedBy[0]);
358
+
359
+        $this->_em->clear();
360
+
361
+        $dql = "SELECT t, e FROM Doctrine\Tests\Models\DDC117\DDC117Translation t ".
362
+               "JOIN t.reviewedByEditors e WHERE t.article = ?1 AND t.language = ?2";
363
+        $trans = $this->_em->createQuery($dql)
364
+                           ->setParameter(1, $this->translation->getArticleId())
365
+                           ->setParameter(2, $this->translation->getLanguage())
366
+                           ->getSingleResult();
367
+
368
+        $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Translation', $trans);
369
+        $this->assertContainsOnly('Doctrine\Tests\Models\DDC117\DDC117Editor', $trans->reviewedByEditors);
370
+        $this->assertEquals(1, count($trans->reviewedByEditors));
371
+    }
372
+
373
+    /**
374
+     * @group DDC-117
375
+     */
376
+    public function testLoadOneToManyOfSourceEntityWithAssociationIdentifier()
377
+    {
378
+        $editor = $this->loadEditorFixture();
379
+
380
+        $editor->addLastTranslation($editor->reviewingTranslations[0]);
381
+        $this->_em->flush();
382
+        $this->_em->clear();
383
+
384
+        $editor = $this->_em->find(get_class($editor), $editor->id);
385
+        $lastTranslatedBy = $editor->reviewingTranslations[0]->getLastTranslatedBy();
386
+        $lastTranslatedBy->count();
387
+
388
+        $this->assertEquals(1, count($lastTranslatedBy));
389
+    }
390
+
391
+    /**
392
+     * @return DDC117Editor
393
+     */
394
+    private function loadEditorFixture()
395
+    {
396
+        $editor = new DDC117Editor("beberlei");
397
+
398
+        /* @var $article1 DDC117Article */
399
+        $article1 = $this->_em->find(get_class($this->article1), $this->article1->id());
400
+        foreach ($article1->getTranslations() AS $translation) {
401
+            $editor->reviewingTranslations[] = $translation;
402
+        }
403
+
404
+        /* @var $article2 DDC117Article */
405
+        $article2 = $this->_em->find(get_class($this->article2), $this->article2->id());
406
+        $article2->addTranslation("de", "Vanille-Krapferl"); // omnomnom
407
+        $article2->addTranslation("fr", "Sorry can't speak french!");
408
+
409
+        foreach ($article2->getTranslations() AS $translation) {
410
+            $this->_em->persist($translation); // otherwise persisting the editor won't work, reachability!
411
+            $editor->reviewingTranslations[] = $translation;
412
+        }
413
+
414
+        $this->_em->persist($editor);
415
+        $this->_em->flush();
416
+        $this->_em->clear();
417
+
418
+        return $this->_em->find(get_class($editor), $editor->id);
419
+    }
420
+
421
+    /**
422
+     * @group DDC-1519
423
+     */
424
+    public function testMergeForeignKeyIdentifierEntity()
425
+    {
426
+        $idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
427
+
428
+        $refRep = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
429
+
430
+        $this->_em->detach($refRep);
431
+        $refRep = $this->_em->merge($refRep);
432
+
433
+        $this->assertEquals($this->article1->id(), $refRep->source()->id());
434
+        $this->assertEquals($this->article2->id(), $refRep->target()->id());
435
+    }
436
+
437
+    /**
438
+     * @group DDC-1652
439
+     */
440
+    public function testArrayHydrationWithCompositeKey()
441
+    {
442
+        $dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
443
+        $before = count($this->_em->createQuery($dql)->getResult());
444
+
445
+        $this->article1 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id());
446
+        $this->article2 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article2->id());
447
+
448
+        $this->reference = new DDC117Reference($this->article2, $this->article1, "Test-Description");
449
+        $this->_em->persist($this->reference);
450
+
451
+        $this->reference = new DDC117Reference($this->article1, $this->article1, "Test-Description");
452
+        $this->_em->persist($this->reference);
453
+
454
+        $this->reference = new DDC117Reference($this->article2, $this->article2, "Test-Description");
455
+        $this->_em->persist($this->reference);
456
+
457
+        $this->_em->flush();
458
+
459
+        $dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
460
+        $data = $this->_em->createQuery($dql)->getArrayResult();
461
+
462
+        $this->assertEquals($before + 3, count($data));
463
+    }
464
+}

文件差異過大導致無法顯示
+ 1098 - 0
vendor/doctrine/orm/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php


+ 142 - 0
vendor/gedmo/doctrine-extensions/README.md 查看文件

@@ -0,0 +1,142 @@
1
+# Doctrine2 behavioral extensions
2
+
3
+## I'm looking for maintainers of this project
4
+
5
+Feel free to open discusion in issue or email message if you are interested in maintaining,
6
+refactoring doctrine2 extensions. The repository can be moved to the maintainers account and fork
7
+left on mine. I do not want users to lose availability of stable extensions which they were and are
8
+used to, at the moment.
9
+
10
+**Version 2.3.5**
11
+
12
+[![Build Status](https://secure.travis-ci.org/l3pp4rd/DoctrineExtensions.png?branch=master)](http://travis-ci.org/l3pp4rd/DoctrineExtensions)
13
+
14
+**Note:** recently doctrine orm and odm were updated to use common doctrine mapping persistense
15
+layer. The support for it has been made and tagged with **2.3.1** version tag. It will be compatible
16
+with latest version of doctrine mapping at master branches
17
+
18
+### Latest updates
19
+
20
+**2013-03-05**
21
+
22
+- A new extension - **References**, which links Entities in Documents and visa versa, [read more about it](https://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/references.md). It was contributed by @jwage, @avalanche123, @jmikola and @bobthecow, thanks
23
+
24
+**2013-02-05**
25
+
26
+- **Sluggable** added back slug handler mapping driver support for yaml and xml.
27
+
28
+**2012-12-06**
29
+
30
+- **Blameable** extension added to allow setting a username string or user object on fields, with the same options as Timestampable.
31
+
32
+
33
+**2012-07-05**
34
+
35
+- **Mapping** drivers were updated to support latest doctrine versions.
36
+
37
+**2012-05-01**
38
+
39
+- **Sluggable** now allows to regenerate slug if its set to empty or null. Also it allows to
40
+manually set the slug, in that case it would only transliterate it and ensure uniqueness.
41
+
42
+### Summary and features
43
+
44
+This package contains extensions for Doctrine2 that hook into the facilities of Doctrine and
45
+offer new functionality or tools to use Doctrine2 more efficently. This package contains mostly
46
+used behaviors which can be easily attached to your event system of Doctrine2 and handle the
47
+records being flushed in the behavioral way. List of extensions:
48
+
49
+- **Tree** - this extension automates the tree handling process and adds some tree specific functions on repository.
50
+(**closure**, **nestedset** or **materialized path**)
51
+- **Translatable** - gives you a very handy solution for translating records into diferent languages. Easy to setup, easier to use.
52
+- **Sluggable** - urlizes your specified fields into single unique slug
53
+- **Timestampable** - updates date fields on create, update and even property change.
54
+- **Blameable** - updates string or reference fields on create, update and even property change with a string or object (e.g. user).
55
+- **Loggable** - helps tracking changes and history of objects, also supports version managment.
56
+- **Sortable** - makes any document or entity sortable
57
+- **Translator** - explicit way to handle translations
58
+- **Softdeleteable** - allows to implicitly remove records
59
+- **Uploadable** - provides file upload handling in entity fields
60
+- **References** - supports linking Entities in Documents and visa versa
61
+
62
+Currently these extensions support **Yaml**, **Annotation**  and **Xml** mapping. Additional mapping drivers
63
+can be easily implemented using Mapping extension to handle the additional metadata mapping.
64
+
65
+**Note:** Please note, that xml mapping needs to be in a different namespace, the declared namespace for
66
+Doctrine extensions is http://gediminasm.org/schemas/orm/doctrine-extensions-mapping
67
+So root node now looks like this:
68
+
69
+**Note:** Use 2.1.x tag in order to use extensions based on Doctrine2.1.x versions. Currently
70
+master branch is based on 2.2.x versions and may not work with 2.1.x
71
+
72
+```
73
+<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
74
+                 xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
75
+...
76
+</doctrine-mapping>
77
+```
78
+
79
+XML mapping xsd schemas are also versioned and can be used by version suffix:
80
+
81
+- Latest version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping**
82
+- 2.2.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-2**
83
+- 2.1.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-1**
84
+
85
+### ODM MongoDB support
86
+
87
+List of extensions which support ODM
88
+
89
+- Translatable
90
+- Sluggable
91
+- Timestampable
92
+- Blameable
93
+- Loggable
94
+- Translator
95
+- Tree (Materialized Path strategy for now)
96
+- References
97
+
98
+All these extensions can be nested together and mapped in traditional ways - annotations,
99
+xml or yaml
100
+
101
+You can test these extensions on [my blog](http://gediminasm.org/demo "Test doctrine behavioral extensions").
102
+All tutorials for basic usage examples are on [my blog](http://gediminasm.org "Tutorials for extensions") too.
103
+You can also fork or clone this blog from [github repository](https://github.com/l3pp4rd/gediminasm.org)
104
+
105
+### Running the tests:
106
+
107
+PHPUnit 3.6 or newer is required.
108
+To setup and run tests follow these steps:
109
+
110
+- go to the root directory of extensions
111
+- download composer: **wget https://getcomposer.org/composer.phar**
112
+- install dev libraries: **php composer.phar install --dev**
113
+- run: **phpunit -c tests**
114
+- optional - run mongodb service if targetting mongo tests
115
+
116
+<a name="example-demo"></a>
117
+
118
+### Running the example:
119
+
120
+To setup and run example follow these steps:
121
+
122
+- go to the root directory of extensions
123
+- download composer: **wget https://getcomposer.org/composer.phar**
124
+- install dev libraries: **php composer.phar install --dev**
125
+- edit **example/em.php** and configure your database on top of the file
126
+- run: **./example/bin/console** or **php example/bin/console** for console commands
127
+- run: **./example/bin/console orm:schema-tool:create** to create schema
128
+- run: **php example/run.php** to run example
129
+
130
+### Contributors:
131
+
132
+Thanks to [everyone participating](http://github.com/l3pp4rd/DoctrineExtensions/contributors) in
133
+the development of these great Doctrine2 extensions!
134
+
135
+And especialy ones who create and maintain new extensions:
136
+
137
+- Lukas Botsch [lbotsch](http://github.com/lbotsch)
138
+- Gustavo Adrian [comfortablynumb](http://github.com/comfortablynumb)
139
+- Boussekeyt Jules [gordonslondon](http://github.com/gordonslondon)
140
+- Kudryashov Konstantin [everzet](http://github.com/everzet)
141
+- David Buchmann [dbu](https://github.com/dbu)
142
+

+ 219 - 0
vendor/gedmo/doctrine-extensions/doc/references.md 查看文件

@@ -0,0 +1,219 @@
1
+# Cross Object Mapper References behavior extension for Doctrine 2
2
+
3
+Create documents and entities that contain references to each other.
4
+
5
+## Options
6
+
7
+The following options are possible on reference one and many associations:
8
+
9
+**Owning Side**
10
+
11
+- **type** - The type of association.
12
+- **class** - The associated class name.
13
+- **inversedBy** - The property name for the inverse side of this association.
14
+- **identifier** - The property name to store the associated object id in.
15
+
16
+**Inverse Side**
17
+
18
+- **type** - The type of association.
19
+- **class** - The associated class name.
20
+- **mappedBy** - The property name for the owning side of this association.
21
+
22
+## Annotations
23
+
24
+**@Gedmo\ReferenceOne**
25
+
26
+``` php
27
+<?php
28
+
29
+/**
30
+ * @Gedmo\ReferenceMany(type="entity", class="Entity\StockItem", mappedBy="product")
31
+ */
32
+private $stockItems;
33
+```
34
+
35
+**@Gedmo\ReferenceMany**
36
+
37
+``` php
38
+<?php
39
+
40
+/**
41
+ * @Gedmo\ReferenceOne(type="document", class="Document\Product", inversedBy="stockItems", identifier="productId")
42
+ */
43
+private $product;
44
+```
45
+
46
+## Example
47
+
48
+Here is an example where you have a Product which is mapped using the Doctrine MongoDB ODM project and it contains a property `$stockItems` that is populated from the Doctrine2 ORM.
49
+
50
+``` php
51
+<?php
52
+
53
+namespace Document;
54
+
55
+use Doctrine\Common\Collections\Collection;
56
+use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
57
+use Gedmo\Mapping\Annotation as Gedmo;
58
+
59
+/**
60
+ * @ODM\Document
61
+ */
62
+class Product
63
+{
64
+    /**
65
+     * @ODM\Id
66
+     */
67
+    private $id;
68
+
69
+    /**
70
+     * @ODM\String
71
+     */
72
+    private $name;
73
+
74
+    /**
75
+     * @Gedmo\ReferenceMany(type="entity", class="Entity\StockItem", mappedBy="product")
76
+     */
77
+    private $stockItems;
78
+
79
+    public function getId()
80
+    {
81
+        return $this->id;
82
+    }
83
+
84
+    public function setId($id)
85
+    {
86
+        $this->id = $id;
87
+    }
88
+
89
+    public function getName()
90
+    {
91
+        return $this->name;
92
+    }
93
+
94
+    public function setName($name)
95
+    {
96
+        $this->name = $name;
97
+    }
98
+
99
+    public function getStockItems()
100
+    {
101
+        return $this->stockItems;
102
+    }
103
+
104
+    public function setStockItems(Collection $stockItems)
105
+    {
106
+        $this->stockItems = $stockItems;
107
+    }
108
+}
109
+```
110
+
111
+The `StockItem` has a reference to the `Product` as well.
112
+
113
+``` php
114
+<?php
115
+
116
+namespace Entity;
117
+
118
+use Doctrine\ORM\Mapping as ORM;
119
+use Gedmo\Mapping\Annotation as Gedmo;
120
+use References\Fixture\ODM\MongoDB\Product;
121
+
122
+/**
123
+ * @ORM\Entity
124
+ */
125
+class StockItem
126
+{
127
+    /**
128
+     * @ORM\Id
129
+     * @ORM\Column(type="integer")
130
+     * @ORM\GeneratedValue(strategy="IDENTITY")
131
+     */
132
+    private $id;
133
+
134
+    /**
135
+     * @ORM\Column
136
+     */
137
+    private $name;
138
+
139
+    /**
140
+     * @ORM\Column
141
+     */
142
+    private $sku;
143
+
144
+    /**
145
+     * @ORM\Column(type="integer")
146
+     */
147
+    private $quantity;
148
+
149
+    /**
150
+     * @Gedmo\ReferenceOne(type="document", class="Document\Product", inversedBy="stockItems", identifier="productId")
151
+     */
152
+    private $product;
153
+
154
+    /**
155
+     * @ORM\Column(type="string")
156
+     */
157
+    private $productId;
158
+
159
+    public function getId()
160
+    {
161
+        return $this->id;
162
+    }
163
+
164
+    public function setId($id)
165
+    {
166
+        $this->id = $id;
167
+    }
168
+
169
+    public function getName()
170
+    {
171
+        return $this->name;
172
+    }
173
+
174
+    public function setName($name)
175
+    {
176
+        $this->name = $name;
177
+    }
178
+
179
+    public function getSku()
180
+    {
181
+        return $this->sku;
182
+    }
183
+
184
+    public function setSku($sku)
185
+    {
186
+        $this->sku = $sku;
187
+    }
188
+
189
+    public function getQuantity()
190
+    {
191
+        return $this->quantity;
192
+    }
193
+
194
+    public function setQuantity($quantity)
195
+    {
196
+        $this->quantity = $quantity;
197
+    }
198
+
199
+    public function setProduct(Product $product)
200
+    {
201
+        $this->product = $product;
202
+    }
203
+
204
+    public function getProduct()
205
+    {
206
+        return $this->product;
207
+    }
208
+
209
+    public function setProductId($productId)
210
+    {
211
+        $this->productId = $productId;
212
+    }
213
+
214
+    public function getProductId()
215
+    {
216
+        return $this->productId;
217
+    }
218
+}
219
+```

文件差異過大導致無法顯示
+ 1265 - 0
vendor/gedmo/doctrine-extensions/doc/tree.md


+ 95 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php 查看文件

@@ -0,0 +1,95 @@
1
+<?php
2
+
3
+namespace Gedmo\Blameable;
4
+
5
+use Doctrine\Common\EventArgs;
6
+use Doctrine\Common\NotifyPropertyChanged;
7
+use Gedmo\Exception\InvalidArgumentException;
8
+use Gedmo\Mapping\MappedEventSubscriber;
9
+use Gedmo\Timestampable\TimestampableListener;
10
+use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
11
+
12
+/**
13
+ * The Blameable listener handles the update of
14
+ * dates on creation and update.
15
+ *
16
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
17
+ * @package Gedmo.Blameable
18
+ * @subpackage BlameableListener
19
+ * @link http://www.gediminasm.org
20
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21
+ */
22
+class BlameableListener extends TimestampableListener
23
+{
24
+    protected $user;
25
+
26
+    /**
27
+     * Get the user value to set on a blameable field
28
+     *
29
+     * @param object $meta
30
+     * @param string $field
31
+     * @return mixed
32
+     */
33
+    public function getUserValue($meta, $field)
34
+    {
35
+        if ($meta->hasAssociation($field)) {
36
+            if (null !== $this->user && ! is_object($this->user)) {
37
+                throw new InvalidArgumentException("Blame is reference, user must be an object");
38
+            }
39
+
40
+            return $this->user;
41
+        }
42
+
43
+        // ok so its not an association, then it is a string
44
+        if (is_object($this->user)) {
45
+            if (method_exists($this->user, 'getUsername')) {
46
+                return (string)$this->user->getUsername();
47
+            }
48
+            if (method_exists($this->user, '__toString')) {
49
+                return $this->user->__toString();
50
+            }
51
+            throw new InvalidArgumentException("Field expects string, user must be a string, or object should have method getUsername or __toString");
52
+        }
53
+
54
+        return $this->user;
55
+    }
56
+
57
+    /**
58
+     * Set a user value to return
59
+     *
60
+     * @return mixed
61
+     */
62
+    public function setUserValue($user)
63
+    {
64
+        $this->user = $user;
65
+    }
66
+
67
+    /**
68
+     * {@inheritDoc}
69
+     */
70
+    protected function getNamespace()
71
+    {
72
+        return __NAMESPACE__;
73
+    }
74
+
75
+    /**
76
+     * Updates a field
77
+     *
78
+     * @param mixed $object
79
+     * @param BlameableAdapter $ea
80
+     * @param $meta
81
+     * @param $field
82
+     */
83
+    protected function updateField($object, $ea, $meta, $field)
84
+    {
85
+        $property = $meta->getReflectionProperty($field);
86
+        $oldValue = $property->getValue($object);
87
+        $newValue = $this->getUserValue($meta, $field);
88
+
89
+        $property->setValue($object, $newValue);
90
+        if ($object instanceof NotifyPropertyChanged) {
91
+            $uow = $ea->getObjectManager()->getUnitOfWork();
92
+            $uow->propertyChanged($object, $field, $oldValue, $newValue);
93
+        }
94
+    }
95
+}

+ 25 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php 查看文件

@@ -0,0 +1,25 @@
1
+<?php
2
+
3
+namespace Gedmo\Mapping\Annotation;
4
+
5
+use Doctrine\Common\Annotations\Annotation;
6
+
7
+/**
8
+ * Reference annotation for ORM -> ODM references extension
9
+ * to be user like @ReferenceMany(type="entity", class="MyEntity", identifier="entity_id")
10
+ *
11
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
12
+ * @package Gedmo.Mapping.Annotation
13
+ * @subpackage Language
14
+ * @link http://www.gediminasm.org
15
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
16
+ * @Annotation
17
+ */
18
+abstract class Reference extends Annotation
19
+{
20
+    public $type;
21
+    public $class;
22
+    public $identifier;
23
+    public $mappedBy;
24
+    public $inversedBy;
25
+}

+ 21 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php 查看文件

@@ -0,0 +1,21 @@
1
+<?php
2
+
3
+namespace Gedmo\Mapping\Annotation;
4
+
5
+use Doctrine\Common\Annotations\Annotation;
6
+
7
+/**
8
+ * Reference annotation for ORM -> ODM references extension
9
+ * to be user like @ReferenceOne(type="entity", class="MyEntity", identifier="entity_id")
10
+ *
11
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
12
+ * @package Gedmo.Mapping.Annotation
13
+ * @subpackage Language
14
+ * @link http://www.gediminasm.org
15
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
16
+ * @Annotation
17
+ */
18
+class ReferenceMany extends Reference
19
+{
20
+}
21
+

+ 21 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceOne.php 查看文件

@@ -0,0 +1,21 @@
1
+<?php
2
+
3
+namespace Gedmo\Mapping\Annotation;
4
+
5
+use Doctrine\Common\Annotations\Annotation;
6
+
7
+/**
8
+ * Reference annotation for ORM -> ODM references extension
9
+ * to be user like @ReferenceOne(type="entity", class="MyEntity", identifier="entity_id")
10
+ *
11
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
12
+ * @package Gedmo.Mapping.Annotation
13
+ * @subpackage Language
14
+ * @link http://www.gediminasm.org
15
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
16
+ * @Annotation
17
+ */
18
+class ReferenceOne extends Reference
19
+{
20
+}
21
+

+ 31 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php 查看文件

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Gedmo\Mapping\Annotation;
4
+
5
+use Doctrine\Common\Annotations\Annotation;
6
+
7
+/**
8
+ * TreePath annotation for Tree behavioral extension
9
+ *
10
+ * @Annotation
11
+ * @Target("PROPERTY")
12
+ *
13
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
14
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
15
+ * @author <rocco@roccosportal.com>
16
+ * @package Gedmo.Mapping.Annotation
17
+ * @subpackage TreePath
18
+ * @link http://www.gediminasm.org
19
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20
+ */
21
+final class TreePath extends Annotation
22
+{
23
+    public $separator = ',';
24
+
25
+    public $appendId = null;
26
+
27
+    public $startsWithSeparator = false;
28
+
29
+    public $endsWithSeparator = true;
30
+}
31
+

+ 22 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php 查看文件

@@ -0,0 +1,22 @@
1
+<?php
2
+
3
+namespace Gedmo\Mapping\Annotation;
4
+
5
+use Doctrine\Common\Annotations\Annotation;
6
+
7
+/**
8
+ * TreePath annotation for Tree behavioral extension
9
+ *
10
+ * @Annotation
11
+ * @Target("PROPERTY")
12
+ *
13
+ * @author <rocco@roccosportal.com>
14
+ * @package Gedmo.Mapping.Annotation
15
+ * @subpackage TreePathHash
16
+ * @link http://www.gediminasm.org
17
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
18
+ */
19
+final class TreePathHash extends Annotation
20
+{
21
+
22
+}

+ 215 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php 查看文件

@@ -0,0 +1,215 @@
1
+<?php
2
+
3
+namespace Gedmo\References;
4
+
5
+use Doctrine\Common\Collections\ArrayCollection;
6
+
7
+use Doctrine\Common\Collections\Collection;
8
+
9
+/**
10
+ * Lazy collection for loading reference many associations.
11
+ *
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
14
+ * @author Jonathan H. Wage <jonwage@gmail.com>
15
+ * @package Gedmo\References\Mapping\Event
16
+ * @link http://www.gediminasm.org
17
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
18
+ */
19
+class LazyCollection implements Collection
20
+{
21
+    private $results;
22
+    private $callback;
23
+
24
+    public function __construct($callback)
25
+    {
26
+        $this->callback = $callback;
27
+    }
28
+
29
+    public function add($element)
30
+    {
31
+        $this->initialize();
32
+        return $this->results->add($element);
33
+    }
34
+
35
+    public function clear()
36
+    {
37
+        $this->initialize();
38
+        return $this->results->clear();
39
+    }
40
+
41
+    public function contains($element)
42
+    {
43
+        $this->initialize();
44
+        return $this->results->contains($element);
45
+    }
46
+
47
+    public function containsKey($key)
48
+    {
49
+        $this->initialize();
50
+        return $this->results->containsKey($key);
51
+    }
52
+
53
+    public function current()
54
+    {
55
+        $this->initialize();
56
+        return $this->results->current();
57
+    }
58
+
59
+    public function exists(\Closure $p)
60
+    {
61
+        $this->initialize();
62
+        return $this->results->exists($p);
63
+    }
64
+
65
+    public function filter(\Closure $p)
66
+    {
67
+        $this->initialize();
68
+        return $this->results->filter($p);
69
+    }
70
+
71
+    public function first()
72
+    {
73
+        $this->initialize();
74
+        return $this->results->first();
75
+    }
76
+
77
+    public function forAll(\Closure $p)
78
+    {
79
+        $this->initialize();
80
+        return $this->results->forAll($p);
81
+    }
82
+
83
+    public function get($key)
84
+    {
85
+        $this->initialize();
86
+        return $this->results->get($key);
87
+    }
88
+
89
+    public function getKeys()
90
+    {
91
+        $this->initialize();
92
+        return $this->results->getKeys();
93
+    }
94
+
95
+    public function getValues()
96
+    {
97
+        $this->initialize();
98
+        return $this->results->getValues();
99
+    }
100
+
101
+    public function indexOf($element)
102
+    {
103
+        $this->initialize();
104
+        return $this->results->indexOf($element);
105
+    }
106
+
107
+    public function isEmpty()
108
+    {
109
+        $this->initialize();
110
+        return $this->results->isEmpty();
111
+    }
112
+
113
+    public function key()
114
+    {
115
+        $this->initialize();
116
+        return $this->results->key();
117
+    }
118
+
119
+    public function last()
120
+    {
121
+        $this->initialize();
122
+        return $this->results->last();
123
+    }
124
+
125
+    public function map(\Closure $func)
126
+    {
127
+        $this->initialize();
128
+        return $this->results->map($func);
129
+    }
130
+
131
+    public function next()
132
+    {
133
+        $this->initialize();
134
+        return $this->results->next();
135
+    }
136
+
137
+    public function partition(\Closure $p)
138
+    {
139
+        $this->initialize();
140
+        return $this->results->partition($p);
141
+    }
142
+
143
+    public function remove($key)
144
+    {
145
+        $this->initialize();
146
+        return $this->results->remove($key);
147
+    }
148
+
149
+    public function removeElement($element)
150
+    {
151
+        $this->initialize();
152
+        return $this->results->removeElement($element);
153
+    }
154
+
155
+    public function set($key, $value)
156
+    {
157
+        $this->initialize();
158
+        return $this->results->set($key, $value);
159
+    }
160
+
161
+    public function slice($offset, $length = null)
162
+    {
163
+        $this->initialize();
164
+        return $this->results->slice($offset, $length);
165
+    }
166
+
167
+    public function toArray()
168
+    {
169
+        $this->initialize();
170
+        return $this->results->toArray();
171
+    }
172
+
173
+    public function offsetExists($offset)
174
+    {
175
+        $this->initialize();
176
+        return $this->results->offsetExists($offset);
177
+    }
178
+
179
+    public function offsetGet($offset)
180
+    {
181
+        $this->initialize();
182
+        return $this->results->offsetGet($offset);
183
+    }
184
+
185
+    public function offsetSet($offset, $value)
186
+    {
187
+        $this->initialize();
188
+        return $this->results->offsetSet($offset, $value);
189
+    }
190
+
191
+    public function offsetUnset($offset)
192
+    {
193
+        $this->initialize();
194
+        return $this->results->offsetUnset($offset);
195
+    }
196
+
197
+    public function getIterator()
198
+    {
199
+        $this->initialize();
200
+        return $this->results->getIterator();
201
+    }
202
+
203
+    public function count()
204
+    {
205
+        $this->initialize();
206
+        return $this->results->count();
207
+    }
208
+
209
+    private function initialize()
210
+    {
211
+        if (null === $this->results) {
212
+            $this->results = call_user_func($this->callback);
213
+        }
214
+    }
215
+}

+ 99 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php 查看文件

@@ -0,0 +1,99 @@
1
+<?php
2
+
3
+namespace Gedmo\References\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\AnnotationDriverInterface,
6
+    Doctrine\Common\Annotations\AnnotationReader,
7
+    Doctrine\Common\Persistence\Mapping\ClassMetadata,
8
+    Gedmo\Exception\InvalidMappingException;
9
+
10
+/**
11
+ * This is an annotation mapping driver for References
12
+ * behavioral extension.
13
+ *
14
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
15
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
16
+ * @author Jonathan H. Wage <jonwage@gmail.com>
17
+ * @package Gedmo.References.Mapping.Driver
18
+ * @subpackage Annotation
19
+ * @link http://www.gediminasm.org
20
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21
+ */
22
+class Annotation implements AnnotationDriverInterface
23
+{
24
+    /**
25
+     * Annotation to mark field as reference to one
26
+     */
27
+    const REFERENCE_ONE = 'Gedmo\\Mapping\\Annotation\\ReferenceOne';
28
+
29
+    /**
30
+     * Annotation to mark field as reference to many
31
+     */
32
+    const REFERENCE_MANY = 'Gedmo\\Mapping\\Annotation\\ReferenceMany';
33
+
34
+    private $annotations = array(
35
+        'referenceOne'  => self::REFERENCE_ONE,
36
+        'referenceMany' => self::REFERENCE_MANY,
37
+    );
38
+
39
+    /**
40
+     * Annotation reader instance
41
+     *
42
+     * @var object
43
+     */
44
+    private $reader;
45
+
46
+    /**
47
+     * original driver if it is available
48
+     */
49
+    protected $_originalDriver = null;
50
+
51
+    /**
52
+     * {@inheritDoc}
53
+     */
54
+    public function setAnnotationReader($reader)
55
+    {
56
+        $this->reader = $reader;
57
+    }
58
+
59
+    /**
60
+     * {@inheritDoc}
61
+     */
62
+    public function readExtendedMetadata($meta, array &$config)
63
+    {
64
+        $class = $meta->getReflectionClass();
65
+        foreach($this->annotations as $key => $annotation) {
66
+            $config[$key] = array();
67
+            foreach ($class->getProperties() as $property) {
68
+                if ($meta->isMappedSuperclass && !$property->isPrivate() ||
69
+                    $meta->isInheritedField($property->name) ||
70
+                    isset($meta->associationMappings[$property->name]['inherited'])
71
+                ) {
72
+                    continue;
73
+                }
74
+
75
+                if ($reference = $this->reader->getPropertyAnnotation($property, $annotation)) {
76
+                    $config[$key][$property->getName()] = array(
77
+                        'field'      => $property->getName(),
78
+                        'type'       => $reference->type,
79
+                        'class'      => $reference->class,
80
+                        'identifier' => $reference->identifier,
81
+                        'mappedBy'   => $reference->mappedBy,
82
+                        'inversedBy' => $reference->inversedBy,
83
+                    );
84
+                }
85
+            }
86
+        }
87
+    }
88
+
89
+    /**
90
+     * Passes in the mapping read by original driver
91
+     *
92
+     * @param $driver
93
+     * @return void
94
+     */
95
+    public function setOriginalDriver($driver)
96
+    {
97
+        $this->_originalDriver = $driver;
98
+    }
99
+}

+ 99 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php 查看文件

@@ -0,0 +1,99 @@
1
+<?php
2
+
3
+namespace Gedmo\References\Mapping\Event\Adapter;
4
+
5
+use Doctrine\Common\Persistence\ObjectManager;
6
+use Doctrine\ODM\MongoDB\DocumentManager;
7
+use Doctrine\ODM\MongoDB\Proxy\Proxy as MongoDBProxy;
8
+use Doctrine\ORM\EntityManager;
9
+use Doctrine\ORM\Proxy\Proxy as ORMProxy;
10
+use Gedmo\Exception\InvalidArgumentException;
11
+use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
12
+use Gedmo\References\Mapping\Event\ReferencesAdapter;
13
+
14
+/**
15
+ * Doctrine event adapter for ODM references behavior
16
+ *
17
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
18
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
19
+ * @author Jonathan H. Wage <jonwage@gmail.com>
20
+ * @package Gedmo\References\Mapping\Event\Adapter
21
+ * @subpackage ODM
22
+ * @link http://www.gediminasm.org
23
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
24
+ */
25
+final class ODM extends BaseAdapterODM implements ReferencesAdapter
26
+{
27
+    /**
28
+     * @inheritDoc
29
+     */
30
+    public function getIdentifier($om, $object, $single = true)
31
+    {
32
+        if ($om instanceof DocumentManager) {
33
+            return $this->extractIdentifier($om, $object, $single);
34
+        }
35
+
36
+        if ($om instanceof EntityManager) {
37
+            if ($object instanceof ORMProxy) {
38
+                $id = $om->getUnitOfWork()->getEntityIdentifier($object);
39
+            } else {
40
+                $meta = $om->getClassMetadata(get_class($object));
41
+                $id = array();
42
+                foreach ($meta->identifier as $name) {
43
+                    $id[$name] = $meta->getReflectionProperty($name)->getValue($object);
44
+                    // return null if one of identifiers is missing
45
+                    if (!$id[$name]) {
46
+                        return null;
47
+                    }
48
+                }
49
+            }
50
+
51
+            if ($single) {
52
+                $id = current($id);
53
+            }
54
+
55
+            return $id;
56
+        }
57
+    }
58
+
59
+    /**
60
+     * @inheritDoc
61
+     */
62
+    public function getSingleReference($om, $class, $identifier)
63
+    {
64
+        $this->throwIfNotEntityManager($om);
65
+        $meta = $om->getClassMetadata($class);
66
+
67
+        if (!$meta->isInheritanceTypeNone()) {
68
+            return $om->find($class, $identifier);
69
+        }
70
+
71
+        return $om->getReference($class, $identifier);
72
+    }
73
+
74
+    /**
75
+     * @inheritDoc
76
+     */
77
+    public function extractIdentifier($om, $object, $single = true)
78
+    {
79
+        $meta = $om->getClassMetadata(get_class($object));
80
+        if ($object instanceof MongoDBProxy) {
81
+            $id = $om->getUnitOfWork()->getDocumentIdentifier($object);
82
+        } else {
83
+            $id = $meta->getReflectionProperty($meta->identifier)->getValue($object);
84
+        }
85
+
86
+        if ($single || !$id) {
87
+            return $id;
88
+        } else {
89
+            return array($meta->identifier => $id);
90
+        }
91
+    }
92
+
93
+    /**
94
+     * Override so we don't get an exception. We want to allow this.
95
+     */
96
+    private function throwIfNotEntityManager(EntityManager $em)
97
+    {
98
+    }
99
+}

+ 98 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php 查看文件

@@ -0,0 +1,98 @@
1
+<?php
2
+
3
+namespace Gedmo\References\Mapping\Event\Adapter;
4
+
5
+use Doctrine\Common\Persistence\ObjectManager;
6
+use Doctrine\ODM\MongoDB\DocumentManager;
7
+use Doctrine\ODM\MongoDB\Proxy\Proxy as MongoDBProxy;
8
+use Doctrine\ORM\EntityManager;
9
+use Doctrine\ORM\Proxy\Proxy as ORMProxy;
10
+use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
11
+use Gedmo\References\Mapping\Event\ReferencesAdapter;
12
+
13
+/**
14
+ * Doctrine event adapter for ORM references behavior
15
+ *
16
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
17
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
18
+ * @author Jonathan H. Wage <jonwage@gmail.com>
19
+ * @package Gedmo\References\Mapping\Event\Adapter
20
+ * @subpackage ORM
21
+ * @link http://www.gediminasm.org
22
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
23
+ */
24
+final class ORM extends BaseAdapterORM implements ReferencesAdapter
25
+{
26
+    /**
27
+     * @inheritDoc
28
+     */
29
+    public function getIdentifier($om, $object, $single = true)
30
+    {
31
+        if ($om instanceof EntityManager) {
32
+            return $this->extractIdentifier($om, $object, $single);
33
+        }
34
+
35
+        if ($om instanceof DocumentManager) {
36
+            $meta = $om->getClassMetadata(get_class($object));
37
+            if ($object instanceof MongoDBProxy) {
38
+                $id = $om->getUnitOfWork()->getDocumentIdentifier($object);
39
+            } else {
40
+                $id = $meta->getReflectionProperty($meta->identifier)->getValue($object);
41
+            }
42
+
43
+            if ($single || !$id) {
44
+                return $id;
45
+            }
46
+
47
+            return array($meta->identifier => $id);
48
+        }
49
+    }
50
+
51
+    /**
52
+     * @inheritDoc
53
+     */
54
+    public function getSingleReference($om, $class, $identifier)
55
+    {
56
+        $this->throwIfNotDocumentManager($om);
57
+        $meta = $om->getClassMetadata($class);
58
+
59
+        if (!$meta->isInheritanceTypeNone()) {
60
+            return $om->find($class, $identifier);
61
+        }
62
+
63
+        return $om->getReference($class, $identifier);
64
+    }
65
+
66
+    /**
67
+     * @inheritDoc
68
+     */
69
+    public function extractIdentifier($om, $object, $single = true)
70
+    {
71
+        if ($object instanceof ORMProxy) {
72
+            $id = $om->getUnitOfWork()->getEntityIdentifier($object);
73
+        } else {
74
+            $meta = $om->getClassMetadata(get_class($object));
75
+            $id = array();
76
+            foreach ($meta->identifier as $name) {
77
+                $id[$name] = $meta->getReflectionProperty($name)->getValue($object);
78
+                // return null if one of identifiers is missing
79
+                if (!$id[$name]) {
80
+                    return null;
81
+                }
82
+            }
83
+        }
84
+
85
+        if ($single) {
86
+            $id = current($id);
87
+        }
88
+
89
+        return $id;
90
+    }
91
+
92
+    /**
93
+     * Override so we don't get an exception. We want to allow this.
94
+     */
95
+    private function throwIfNotDocumentManager(DocumentManager $dm)
96
+    {
97
+    }
98
+}

+ 50 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php 查看文件

@@ -0,0 +1,50 @@
1
+<?php
2
+
3
+namespace Gedmo\References\Mapping\Event;
4
+
5
+use Doctrine\Common\Persistence\Mapping\ClassMetadata;
6
+use Doctrine\Common\Persistence\ObjectManager;
7
+use Gedmo\Mapping\Event\AdapterInterface;
8
+
9
+/**
10
+ * Doctrine event adapter interface for References behavior
11
+ *
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
14
+ * @author Jonathan H. Wage <jonwage@gmail.com>
15
+ * @package Gedmo\References\Mapping\Event
16
+ * @subpackage ReferencesAdapter
17
+ * @link http://www.gediminasm.org
18
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19
+ */
20
+interface ReferencesAdapter extends AdapterInterface
21
+{
22
+    /**
23
+     * Gets the identifier of the given object using the passed ObjectManager.
24
+     *
25
+     * @param ObjectManager $om
26
+     * @param object $object
27
+     * @param bool $single
28
+     * @return array|scalar $id - array or single identifier
29
+     */
30
+    function getIdentifier($om, $object, $single = true);
31
+
32
+    /**
33
+     * Gets a single reference for the given ObjectManager, class and identifier.
34
+     *
35
+     * @param ObjectManager $om
36
+     * @param string $class
37
+     * @param array|scalar $identifier
38
+     **/
39
+    function getSingleReference($om, $class, $identifier);
40
+
41
+    /**
42
+     * Extracts identifiers from object or proxy.
43
+     *
44
+     * @param DocumentManager $om
45
+     * @param object $object
46
+     * @param bool $single
47
+     * @return array|scalar - array or single identifier
48
+     */
49
+    function extractIdentifier($om, $object, $single = true);
50
+}

+ 158 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php 查看文件

@@ -0,0 +1,158 @@
1
+<?php
2
+
3
+namespace Gedmo\References;
4
+
5
+use Doctrine\Common\Collections\ArrayCollection;
6
+use Doctrine\Common\EventArgs;
7
+use Doctrine\ORM\Mapping\ClassMetadata as ORMClassMetadata;
8
+use Doctrine\ORM\EntityManager;
9
+use Doctrine\ODM\MongoDB\DocumentManager;
10
+use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as MongoDBClassMetadata;
11
+use Gedmo\Exception\InvalidArgumentException;
12
+use Gedmo\Mapping\MappedEventSubscriber;
13
+
14
+/**
15
+ * Listener for loading and persisting cross database references.
16
+ *
17
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
18
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
19
+ * @author Jonathan H. Wage <jonwage@gmail.com>
20
+ * @package Gedmo\References\Mapping\Event
21
+ * @subpackage MappedEventSubscriber
22
+ * @link http://www.gediminasm.org
23
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
24
+ */
25
+class ReferencesListener extends MappedEventSubscriber
26
+{
27
+    private $managers;
28
+
29
+    public function __construct(array $managers = array())
30
+    {
31
+        $this->managers = $managers;
32
+    }
33
+
34
+    public function loadClassMetadata(EventArgs $eventArgs)
35
+    {
36
+        $ea = $this->getEventAdapter($eventArgs);
37
+        $this->loadMetadataForObjectClass(
38
+            $ea->getObjectManager(), $eventArgs->getClassMetadata()
39
+        );
40
+    }
41
+
42
+    public function postLoad(EventArgs $eventArgs)
43
+    {
44
+        $ea = $this->getEventAdapter($eventArgs);
45
+        $om = $ea->getObjectManager();
46
+        $object = $ea->getObject();
47
+        $meta = $om->getClassMetadata(get_class($object));
48
+        $config = $this->getConfiguration($om, $meta->name);
49
+        foreach ($config['referenceOne'] as $mapping) {
50
+            $property = $meta->reflClass->getProperty($mapping['field']);
51
+            $property->setAccessible(true);
52
+            if (isset($mapping['identifier'])) {
53
+                $referencedObjectId = $meta->getFieldValue($object, $mapping['identifier']);
54
+                if (null !== $referencedObjectId) {
55
+                    $property->setValue(
56
+                        $object,
57
+                        $ea->getSingleReference(
58
+                            $this->getManager($mapping['type']),
59
+                            $mapping['class'],
60
+                            $referencedObjectId
61
+                        )
62
+                    );
63
+                }
64
+            }
65
+        }
66
+
67
+        foreach ($config['referenceMany'] as $mapping) {
68
+            $property = $meta->reflClass->getProperty($mapping['field']);
69
+            $property->setAccessible(true);
70
+            if (isset($mapping['mappedBy'])) {
71
+                $id = $ea->extractIdentifier($om, $object);
72
+                $manager = $this->getManager($mapping['type']);
73
+                $class = $mapping['class'];
74
+                $refMeta = $manager->getClassMetadata($class);
75
+                $refConfig = $this->getConfiguration($manager, $refMeta->name);
76
+                if (isset($refConfig['referenceOne'][$mapping['mappedBy']])) {
77
+                    $refMapping = $refConfig['referenceOne'][$mapping['mappedBy']];
78
+                    $identifier = $refMapping['identifier'];
79
+                    $property->setValue(
80
+                        $object,
81
+                        new LazyCollection(
82
+                            function() use ($id, &$manager, $class, $identifier) {
83
+                                $results = $manager
84
+                                    ->getRepository($class)
85
+                                    ->findBy(array(
86
+                                        $identifier => $id,
87
+                                    ));
88
+
89
+                                return new ArrayCollection((is_array($results) ? $results : $results->toArray()));
90
+                            }
91
+                        )
92
+                    );
93
+                }
94
+            }
95
+        }
96
+    }
97
+
98
+    public function prePersist(EventArgs $eventArgs)
99
+    {
100
+        $this->updateReferences($eventArgs);
101
+    }
102
+
103
+    public function preUpdate(EventArgs $eventArgs)
104
+    {
105
+        $this->updateReferences($eventArgs);
106
+    }
107
+
108
+    public function getSubscribedEvents()
109
+    {
110
+        return array(
111
+            'postLoad',
112
+            'loadClassMetadata',
113
+            'prePersist',
114
+            'preUpdate',
115
+        );
116
+    }
117
+
118
+    public function registerManager($type, $manager)
119
+    {
120
+        $this->managers[$type] = $manager;
121
+    }
122
+
123
+    public function getManager($type)
124
+    {
125
+        return $this->managers[$type];
126
+    }
127
+
128
+    protected function getNamespace()
129
+    {
130
+        return __NAMESPACE__;
131
+    }
132
+
133
+    private function updateReferences(EventArgs $eventArgs)
134
+    {
135
+        $ea = $this->getEventAdapter($eventArgs);
136
+        $om = $ea->getObjectManager();
137
+        $object = $ea->getObject();
138
+        $meta = $om->getClassMetadata(get_class($object));
139
+        $config = $this->getConfiguration($om, $meta->name);
140
+        foreach ($config['referenceOne'] as $mapping) {
141
+            if (isset($mapping['identifier'])) {
142
+                $property = $meta->reflClass->getProperty($mapping['field']);
143
+                $property->setAccessible(true);
144
+                $referencedObject = $property->getValue($object);
145
+                if (is_object($referencedObject)) {
146
+                    $meta->setFieldValue(
147
+                        $object,
148
+                        $mapping['identifier'],
149
+                        $ea->getIdentifier(
150
+                            $this->getManager($mapping['type']),
151
+                            $referencedObject
152
+                        )
153
+                    );
154
+                }
155
+            }
156
+        }
157
+    }
158
+}

+ 50 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php 查看文件

@@ -0,0 +1,50 @@
1
+<?php
2
+
3
+namespace Gedmo\SoftDeleteable\Mapping;
4
+
5
+use Gedmo\Exception\InvalidMappingException;
6
+use Doctrine\Common\Persistence\Mapping\ClassMetadata;
7
+
8
+/**
9
+ * This class is used to validate mapping information
10
+ *
11
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @package Gedmo.SoftDeleteable.Mapping
14
+ * @subpackage Validator
15
+ * @link http://www.gediminasm.org
16
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
17
+ */
18
+
19
+class Validator
20
+{
21
+    /**
22
+     * List of types which are valid for timestamp
23
+     *
24
+     * @var array
25
+     */
26
+    public static $validTypes = array(
27
+        'date',
28
+        'time',
29
+        'datetime',
30
+        'datetimetz',
31
+        'timestamp',
32
+        'zenddate'
33
+    );
34
+
35
+
36
+    public static function validateField(ClassMetadata $meta, $field)
37
+    {
38
+        if ($meta->isMappedSuperclass) {
39
+            return;
40
+        }
41
+
42
+        $fieldMapping = $meta->getFieldMapping($field);
43
+
44
+        if (!in_array($fieldMapping['type'], self::$validTypes)) {
45
+            throw new InvalidMappingException(sprintf('Field "%s" must be of one of the following types: "%s"',
46
+                $fieldMapping['type'],
47
+                implode(', ', self::$validTypes)));
48
+        }
49
+    }
50
+}

+ 486 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php 查看文件

@@ -0,0 +1,486 @@
1
+<?php
2
+
3
+namespace Gedmo\Sortable;
4
+
5
+use Doctrine\Common\EventArgs;
6
+use Gedmo\Mapping\MappedEventSubscriber;
7
+use Gedmo\Sluggable\Mapping\Event\SortableAdapter;
8
+use Doctrine\ORM\Proxy\Proxy;
9
+
10
+/**
11
+ * The SortableListener maintains a sort index on your entities
12
+ * to enable arbitrary sorting.
13
+ *
14
+ * This behavior can impact the performance of your application
15
+ * since it does some additional calculations on persisted objects.
16
+ *
17
+ * @author Lukas Botsch <lukas.botsch@gmail.com>
18
+ * @subpackage SortableListener
19
+ * @package Gedmo.Sortable
20
+ * @link http://www.gediminasm.org
21
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
22
+ */
23
+class SortableListener extends MappedEventSubscriber
24
+{
25
+    private $relocations = array();
26
+    private $maxPositions = array();
27
+
28
+    /**
29
+     * Specifies the list of events to listen
30
+     *
31
+     * @return array
32
+     */
33
+    public function getSubscribedEvents()
34
+    {
35
+        return array(
36
+            'onFlush',
37
+            'loadClassMetadata',
38
+            'prePersist',
39
+        );
40
+    }
41
+
42
+    /**
43
+     * Maps additional metadata
44
+     *
45
+     * @param EventArgs $args
46
+     */
47
+    public function loadClassMetadata(EventArgs $args)
48
+    {
49
+        $ea = $this->getEventAdapter($args);
50
+        $this->loadMetadataForObjectClass($ea->getObjectManager(), $args->getClassMetadata());
51
+    }
52
+
53
+    /**
54
+     * Update position on objects being updated during flush
55
+     * if they require changing
56
+     *
57
+     * @param EventArgs $args
58
+     */
59
+    public function onFlush(EventArgs $args)
60
+    {
61
+        $ea = $this->getEventAdapter($args);
62
+        $om = $ea->getObjectManager();
63
+        $uow = $om->getUnitOfWork();
64
+
65
+        // process all objects being deleted
66
+        foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
67
+            $meta = $om->getClassMetadata(get_class($object));
68
+            if ($config = $this->getConfiguration($om, $meta->name)) {
69
+                $this->processDeletion($om, $config, $meta, $object);
70
+            }
71
+        }
72
+
73
+        // process all objects being updated
74
+        foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
75
+            $meta = $om->getClassMetadata(get_class($object));
76
+            if ($config = $this->getConfiguration($om, $meta->name)) {
77
+                $this->processUpdate($om, $config, $meta, $object);
78
+            }
79
+        }
80
+
81
+        // process all objects being inserted
82
+        foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
83
+            $meta = $om->getClassMetadata(get_class($object));
84
+            if ($config = $this->getConfiguration($om, $meta->name)) {
85
+                $this->processInsert($om, $config, $meta, $object);
86
+            }
87
+        }
88
+
89
+        $this->processRelocations($om);
90
+    }
91
+
92
+    /**
93
+     * Update maxPositions as needed
94
+     *
95
+     * @param EventArgs $args
96
+     */
97
+    public function prePersist(EventArgs $args)
98
+    {
99
+        $ea = $this->getEventAdapter($args);
100
+        $om = $ea->getObjectManager();
101
+        $uow = $om->getUnitOfWork();
102
+        $object = $ea->getObject();
103
+        $meta = $om->getClassMetadata(get_class($object));
104
+
105
+        if ($config = $this->getConfiguration($om, $meta->name)) {
106
+            // Get groups
107
+            $groups = $this->getGroups($meta, $config, $object);
108
+
109
+            // Get hash
110
+            $hash = $this->getHash($meta, $groups, $object, $config);
111
+
112
+            // Get max position
113
+            if (!isset($this->maxPositions[$hash])) {
114
+                $this->maxPositions[$hash] = $this->getMaxPosition($om, $meta, $config, $object);
115
+            }
116
+        }
117
+    }
118
+
119
+    /**
120
+     * Computes node positions and updates the sort field in memory and in the db
121
+     * @param object $em ObjectManager
122
+     */
123
+    private function processInsert($em, $config, $meta, $object)
124
+    {
125
+        $uow = $em->getUnitOfWork();
126
+        
127
+        $old = $meta->getReflectionProperty($config['position'])->getValue($object);
128
+        $newPosition = $meta->getReflectionProperty($config['position'])->getValue($object);
129
+        
130
+        if (is_null($newPosition)) {
131
+            $newPosition = -1;
132
+        }
133
+
134
+        // Get groups
135
+        $groups = $this->getGroups($meta, $config, $object);
136
+
137
+        // Get hash
138
+        $hash = $this->getHash($meta, $groups, $object, $config);
139
+
140
+        // Get max position
141
+        if (!isset($this->maxPositions[$hash])) {
142
+            $this->maxPositions[$hash] = $this->getMaxPosition($em, $meta, $config, $object);
143
+        }
144
+
145
+        // Compute position if it is negative
146
+        if ($newPosition < 0) {
147
+            $newPosition += $this->maxPositions[$hash] + 2; // position == -1 => append at end of list
148
+            if ($newPosition < 0) $newPosition = 0;
149
+        }
150
+
151
+        // Set position to max position if it is too big
152
+        $newPosition = min(array($this->maxPositions[$hash] + 1, $newPosition));
153
+
154
+        // Compute relocations
155
+        $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, -1, +1);
156
+
157
+        // Apply existing relocations
158
+        $applyDelta = 0;
159
+        if (isset($this->relocations[$hash])) {
160
+            foreach ($this->relocations[$hash]['deltas'] as $delta) {
161
+                if ($delta['start'] <= $newPosition
162
+                        && ($delta['stop'] > $newPosition || $delta['stop'] < 0)) {
163
+                    $applyDelta += $delta['delta'];
164
+                }
165
+            }
166
+        }
167
+        $newPosition += $applyDelta;
168
+
169
+        // Add relocations
170
+        call_user_func_array(array($this, 'addRelocation'), $relocation);
171
+
172
+        // Set new position
173
+        if ($old < 0 || is_null($old)) {
174
+            $meta->getReflectionProperty($config['position'])->setValue($object, $newPosition);
175
+            $uow->recomputeSingleEntityChangeSet($meta, $object);
176
+        }
177
+    }
178
+
179
+    /**
180
+     * Computes node positions and updates the sort field in memory and in the db
181
+     * @param object $em ObjectManager
182
+     */
183
+    private function processUpdate($em, $config, $meta, $object)
184
+    {
185
+        $uow = $em->getUnitOfWork();
186
+
187
+        $changed = false;
188
+        $changeSet = $uow->getEntityChangeSet($object);
189
+
190
+        // Get groups
191
+        $groups = $this->getGroups($meta, $config, $object);
192
+        foreach (array_keys($groups) as $group) {
193
+            $changed = $changed || array_key_exists($group, $changeSet);
194
+        }
195
+
196
+        if (array_key_exists($config['position'], $changeSet)) {
197
+            // position was manually updated
198
+            $oldPosition = $changeSet[$config['position']][0];
199
+            $newPosition = $changeSet[$config['position']][1];
200
+            $changed = $changed || $oldPosition != $newPosition;
201
+        } elseif ($changed) {
202
+            // group has changed, so position has to be recalculated
203
+            $oldPosition = -1;
204
+            $newPosition = -1;
205
+            // specific case
206
+        }
207
+        if (!$changed) return;
208
+
209
+        // Get hash
210
+        $hash = $this->getHash($meta, $groups, $object, $config);
211
+
212
+        // Get max position
213
+        if (!isset($this->maxPositions[$hash])) {
214
+            $this->maxPositions[$hash] = $this->getMaxPosition($em, $meta, $config, $object);
215
+        }
216
+
217
+        // Compute position if it is negative
218
+        if ($newPosition < 0) {
219
+            $newPosition += $this->maxPositions[$hash] + 2; // position == -1 => append at end of list
220
+            if ($newPosition < 0) $newPosition = 0;
221
+        }
222
+
223
+        // Set position to max position if it is too big
224
+        $newPosition = min(array($this->maxPositions[$hash] + 1, $newPosition));
225
+        // Compute relocations
226
+        /*
227
+        CASE 1: shift backwards
228
+        |----0----|----1----|----2----|----3----|----4----|
229
+        |--node1--|--node2--|--node3--|--node4--|--node5--|
230
+        Update node4: setPosition(1)
231
+        --> Update position + 1 where position in [1,3)
232
+        |--node1--|--node4--|--node2--|--node3--|--node5--|
233
+        CASE 2: shift forward
234
+        |----0----|----1----|----2----|----3----|----4----|
235
+        |--node1--|--node2--|--node3--|--node4--|--node5--|
236
+        Update node2: setPosition(3)
237
+        --> Update position - 1 where position in (1,3]
238
+        |--node1--|--node3--|--node4--|--node2--|--node5--|
239
+        */
240
+        $relocation = null;
241
+        if ($oldPosition === -1) {
242
+            // special case when group changes
243
+            $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, -1, +1);
244
+        } elseif ($newPosition < $oldPosition) {
245
+            $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, $oldPosition, +1);
246
+        } elseif ($newPosition > $oldPosition) {
247
+            $relocation = array($hash, $config['useObjectClass'], $groups, $oldPosition + 1, $newPosition + 1, -1);
248
+        }
249
+
250
+        // Apply existing relocations
251
+        $applyDelta = 0;
252
+        if (isset($this->relocations[$hash])) {
253
+            foreach ($this->relocations[$hash]['deltas'] as $delta) {
254
+                if ($delta['start'] <= $newPosition
255
+                        && ($delta['stop'] > $newPosition || $delta['stop'] < 0)) {
256
+                    $applyDelta += $delta['delta'];
257
+                }
258
+            }
259
+        }
260
+        $newPosition += $applyDelta;
261
+
262
+        // Add relocation
263
+        call_user_func_array(array($this, 'addRelocation'), $relocation);
264
+
265
+        // Set new position
266
+        $meta->getReflectionProperty($config['position'])->setValue($object, $newPosition);
267
+        $uow->recomputeSingleEntityChangeSet($meta, $object);
268
+    }
269
+
270
+    /**
271
+     * Computes node positions and updates the sort field in memory and in the db
272
+     * @param object $em ObjectManager
273
+     */
274
+    private function processDeletion($em, $config, $meta, $object)
275
+    {
276
+        $position = $meta->getReflectionProperty($config['position'])->getValue($object);
277
+
278
+        // Get groups
279
+        $groups = $this->getGroups($meta, $config, $object);
280
+
281
+        // Get hash
282
+        $hash = $this->getHash($meta, $groups, $object, $config);
283
+
284
+        // Get max position
285
+        if (!isset($this->maxPositions[$hash])) {
286
+            $this->maxPositions[$hash] = $this->getMaxPosition($em, $meta, $config, $object);
287
+        }
288
+
289
+        // Add relocation
290
+        $this->addRelocation($hash, $config['useObjectClass'], $groups, $position, -1, -1);
291
+    }
292
+
293
+    private function processRelocations($em)
294
+    {
295
+        foreach ($this->relocations as $hash => $relocation) {
296
+            $config = $this->getConfiguration($em, $relocation['name']);
297
+            foreach ($relocation['deltas'] as $delta) {
298
+                if ($delta['start'] > $this->maxPositions[$hash] || $delta['delta'] == 0) {
299
+                    continue;
300
+                }
301
+                $sign = $delta['delta'] < 0 ? "-" : "+";
302
+                $absDelta = abs($delta['delta']);
303
+                $dql = "UPDATE {$relocation['name']} n";
304
+                $dql .= " SET n.{$config['position']} = n.{$config['position']} {$sign} {$absDelta}";
305
+                $dql .= " WHERE n.{$config['position']} >= {$delta['start']}";
306
+                // if not null, false or 0
307
+                if ($delta['stop'] > 0) {
308
+                    $dql .= " AND n.{$config['position']} < {$delta['stop']}";
309
+                }
310
+                $i = -1;
311
+                $params = array();
312
+                foreach ($relocation['groups'] as $group => $value) {
313
+                    if (is_null($value)) {
314
+                        $dql .= " AND n.{$group} IS NULL";
315
+                    } else {
316
+                        $dql .= " AND n.{$group} = :val___".(++$i);
317
+                        $params['val___'.$i] = $value;
318
+                    }
319
+                }
320
+                $q = $em->createQuery($dql);
321
+                $q->setParameters($params);
322
+                $q->getSingleScalarResult();
323
+                $meta = $em->getClassMetadata($relocation['name']);
324
+
325
+                // now walk through the unit of work in memory objects and sync those
326
+                foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $objects) {
327
+                    // for inheritance mapped classes, only root is always in the identity map
328
+                    if ($className !== $meta->rootEntityName || !$this->getConfiguration($em, $className)) {
329
+                        continue;
330
+                    }
331
+                    foreach ($objects as $object) {
332
+                        if ($object instanceof Proxy && !$object->__isInitialized__) {
333
+                            continue;
334
+                        }
335
+                        $oid = spl_object_hash($object);
336
+                        $pos = $meta->getReflectionProperty($config['position'])->getValue($object);
337
+                        $matches = $pos >= $delta['start'];
338
+                        $matches = $matches && ($delta['stop'] <= 0 || $pos < $delta['stop']);
339
+                        $value = reset($relocation['groups']);
340
+                        while ($matches && ($group = key($relocation['groups']))) {
341
+                            $gr = $meta->getReflectionProperty($group)->getValue($object);
342
+                            if (null === $value) {
343
+                                $matches = $gr === null;
344
+                            } else {
345
+                                $matches = $gr === $value;
346
+                            }
347
+                            $value = next($relocation['groups']);
348
+                        }
349
+                        if ($matches) {
350
+                            $meta->getReflectionProperty($config['position'])->setValue($object, $pos + $delta['delta']);
351
+                            $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['position'], $pos + $delta['delta']);
352
+                        }
353
+                    }
354
+                }
355
+            }
356
+        }
357
+
358
+        // Clear relocations
359
+        $this->relocations = array();
360
+        $this->maxPositions = array();
361
+    }
362
+
363
+    private function getHash($meta, $groups, $object, &$config)
364
+    {
365
+        $data = $config['useObjectClass'];
366
+        foreach ($groups as $group => $val) {
367
+            if($val instanceof \DateTime) {
368
+                $val = $val->format('c');
369
+            } elseif (is_object($val)) {
370
+                $val = spl_object_hash($val);
371
+            }
372
+            $data .= $group.$val;
373
+        }
374
+        return md5($data);
375
+    }
376
+
377
+    private function getMaxPosition($em, $meta, $config, $object)
378
+    {
379
+        $uow = $em->getUnitOfWork();
380
+        $maxPos = null;
381
+
382
+        // Get groups
383
+        $groups = $this->getGroups($meta, $config, $object);
384
+
385
+        // Get hash
386
+        $hash = $this->getHash($meta, $groups, $object, $config);
387
+
388
+        // Check for cached max position
389
+        if (isset($this->maxPositions[$hash])) {
390
+            return $this->maxPositions[$hash];
391
+        }
392
+
393
+        // Check for groups that are associations. If the value is an object and is
394
+        // scheduled for insert, it has no identifier yet and is obviously new
395
+        // see issue #226
396
+        foreach ($groups as $group => $val) {
397
+            if (is_object($val) && $uow->isScheduledForInsert($val)) {
398
+                return -1;
399
+            }
400
+        }
401
+
402
+        $groups = isset($config["groups"]) ? $config["groups"] : array();
403
+        $qb = $em->createQueryBuilder();
404
+        $qb->select('MAX(n.'.$config['position'].')')
405
+           ->from($config['useObjectClass'], 'n');
406
+        $qb = $this->addGroupWhere($qb, $groups, $meta, $object);
407
+        $query = $qb->getQuery();
408
+        $query->useQueryCache(false);
409
+        $query->useResultCache(false);
410
+        $res = $query->getResult();
411
+        $maxPos = $res[0][1];
412
+        if (is_null($maxPos)) $maxPos = -1;
413
+        return intval($maxPos);
414
+    }
415
+
416
+    private function addGroupWhere($qb, $groups, $meta, $object)
417
+    {
418
+        $i = 1;
419
+        foreach ($groups as $group) {
420
+            $value = $meta->getReflectionProperty($group)->getValue($object);
421
+            $whereFunc = is_null($qb->getDQLPart('where')) ? 'where' : 'andWhere';
422
+            if (is_null($value)) {
423
+                $qb->{$whereFunc}($qb->expr()->isNull('n.'.$group));
424
+            } else {
425
+                $qb->{$whereFunc}('n.'.$group.' = :group__'.$i);
426
+                $qb->setParameter('group__'.$i, $value);
427
+            }
428
+            $i++;
429
+        }
430
+        return $qb;
431
+    }
432
+
433
+    /**
434
+     * Add a relocation rule
435
+     * @param string $hash The hash of the sorting group
436
+     * @param $meta The objects meta data
437
+     * @param array $groups The sorting groups
438
+     * @param int $start Inclusive index to start relocation from
439
+     * @param int $stop Exclusive index to stop relocation at
440
+     * @param int $delta The delta to add to relocated nodes
441
+     */
442
+    private function addRelocation($hash, $class, $groups, $start, $stop, $delta)
443
+    {
444
+        if (!array_key_exists($hash, $this->relocations)) {
445
+            $this->relocations[$hash] = array('name' => $class, 'groups' => $groups, 'deltas' => array());
446
+        }
447
+
448
+        try {
449
+            $newDelta = array('start' => $start, 'stop' => $stop, 'delta' => $delta);
450
+            array_walk($this->relocations[$hash]['deltas'], function(&$val, $idx, $needle) {
451
+                if ($val['start'] == $needle['start'] && $val['stop'] == $needle['stop']) {
452
+                    $val['delta'] += $needle['delta'];
453
+                    throw new \Exception("Found delta. No need to add it again.");
454
+                }
455
+            }, $newDelta);
456
+            $this->relocations[$hash]['deltas'][] = $newDelta;
457
+        } catch (\Exception $e) {}
458
+    }
459
+
460
+    /**
461
+     * @param $meta
462
+     * @param $config
463
+     * @param $object
464
+     *
465
+     * @return array
466
+     */
467
+    private function getGroups($meta, $config, $object)
468
+    {
469
+        $groups = array();
470
+        if (isset($config['groups'])) {
471
+            foreach ($config['groups'] as $group) {
472
+                $groups[$group] = $meta->getReflectionProperty($group)->getValue($object);
473
+            }
474
+        }
475
+
476
+        return $groups;
477
+    }
478
+
479
+    /**
480
+     * {@inheritDoc}
481
+     */
482
+    protected function getNamespace()
483
+    {
484
+        return __NAMESPACE__;
485
+    }
486
+}

+ 83 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php 查看文件

@@ -0,0 +1,83 @@
1
+<?php
2
+
3
+namespace Gedmo\Timestampable\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\AbstractAnnotationDriver,
6
+    Doctrine\Common\Annotations\AnnotationReader,
7
+    Gedmo\Exception\InvalidMappingException;
8
+
9
+/**
10
+ * This is an annotation mapping driver for Timestampable
11
+ * behavioral extension. Used for extraction of extended
12
+ * metadata from Annotations specificaly for Timestampable
13
+ * extension.
14
+ *
15
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
16
+ * @package Gedmo.Timestampable.Mapping.Driver
17
+ * @subpackage Annotation
18
+ * @link http://www.gediminasm.org
19
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20
+ */
21
+class Annotation extends AbstractAnnotationDriver
22
+{
23
+    /**
24
+     * Annotation field is timestampable
25
+     */
26
+    const TIMESTAMPABLE = 'Gedmo\\Mapping\\Annotation\\Timestampable';
27
+
28
+    /**
29
+     * List of types which are valid for timestamp
30
+     *
31
+     * @var array
32
+     */
33
+    protected $validTypes = array(
34
+        'date',
35
+        'time',
36
+        'datetime',
37
+        'datetimetz',
38
+        'timestamp',
39
+        'zenddate',
40
+        'vardatetime',
41
+        'integer'
42
+    );
43
+
44
+    /**
45
+     * {@inheritDoc}
46
+     */
47
+    public function readExtendedMetadata($meta, array &$config) {
48
+        $class = $this->getMetaReflectionClass($meta);
49
+        // property annotations
50
+        foreach ($class->getProperties() as $property) {
51
+            if ($meta->isMappedSuperclass && !$property->isPrivate() ||
52
+                $meta->isInheritedField($property->name) ||
53
+                isset($meta->associationMappings[$property->name]['inherited'])
54
+            ) {
55
+                continue;
56
+            }
57
+            if ($timestampable = $this->reader->getPropertyAnnotation($property, self::TIMESTAMPABLE)) {
58
+                $field = $property->getName();
59
+                if (!$meta->hasField($field)) {
60
+                    throw new InvalidMappingException("Unable to find timestampable [{$field}] as mapped property in entity - {$meta->name}");
61
+                }
62
+                if (!$this->isValidField($meta, $field)) {
63
+                    throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
64
+                }
65
+                if (!in_array($timestampable->on, array('update', 'create', 'change'))) {
66
+                    throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
67
+                }
68
+                if ($timestampable->on == 'change') {
69
+                    if (!isset($timestampable->field)) {
70
+                        throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
71
+                    }
72
+                    $field = array(
73
+                        'field' => $field,
74
+                        'trackedField' => $timestampable->field,
75
+                        'value' => $timestampable->value,
76
+                    );
77
+                }
78
+                // properties are unique and mapper checks that, no risk here
79
+                $config[$timestampable->on][] = $field;
80
+            }
81
+        }
82
+    }
83
+}

+ 99 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php 查看文件

@@ -0,0 +1,99 @@
1
+<?php
2
+
3
+namespace Gedmo\Timestampable\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\Xml as BaseXml,
6
+    Gedmo\Exception\InvalidMappingException;
7
+
8
+/**
9
+ * This is a xml mapping driver for Timestampable
10
+ * behavioral extension. Used for extraction of extended
11
+ * metadata from xml specificaly for Timestampable
12
+ * extension.
13
+ *
14
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
15
+ * @author Miha Vrhovnik <miha.vrhovnik@gmail.com>
16
+ * @package Gedmo.Timestampable.Mapping.Driver
17
+ * @subpackage Xml
18
+ * @link http://www.gediminasm.org
19
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20
+ */
21
+class Xml extends BaseXml
22
+{
23
+
24
+    /**
25
+     * List of types which are valid for timestamp
26
+     *
27
+     * @var array
28
+     */
29
+    private $validTypes = array(
30
+        'date',
31
+        'time',
32
+        'datetime',
33
+        'datetimetz',
34
+        'timestamp',
35
+        'zenddate',
36
+        'vardatetime',
37
+        'integer'
38
+    );
39
+
40
+    /**
41
+     * {@inheritDoc}
42
+     */
43
+    public function readExtendedMetadata($meta, array &$config)
44
+    {
45
+        /**
46
+         * @var \SimpleXmlElement $mapping
47
+         */
48
+        $mapping = $this->_getMapping($meta->name);
49
+
50
+        if (isset($mapping->field)) {
51
+            /**
52
+             * @var \SimpleXmlElement $fieldMapping
53
+             */
54
+            foreach ($mapping->field as $fieldMapping) {
55
+                $fieldMappingDoctrine = $fieldMapping;
56
+                $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
57
+                if (isset($fieldMapping->timestampable)) {
58
+                    /**
59
+                     * @var \SimpleXmlElement $data
60
+                     */
61
+                    $data = $fieldMapping->timestampable;
62
+
63
+                    $field = $this->_getAttribute($fieldMappingDoctrine, 'name');
64
+                    if (!$this->isValidField($meta, $field)) {
65
+                        throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
66
+                    }
67
+                    if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
68
+                        throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
69
+                    }
70
+
71
+                    if ($this->_getAttribute($data, 'on') == 'change') {
72
+                        if (!$this->_isAttributeSet($data, 'field')) {
73
+                            throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
74
+                        }
75
+                        $field = array(
76
+                            'field' => $field,
77
+                            'trackedField' => $this->_getAttribute($data, 'field'),
78
+                            'value' => $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value') : null,
79
+                        );
80
+                    }
81
+                    $config[$this->_getAttribute($data, 'on')][] = $field;
82
+                }
83
+            }
84
+        }
85
+    }
86
+
87
+    /**
88
+     * Checks if $field type is valid
89
+     *
90
+     * @param object $meta
91
+     * @param string $field
92
+     * @return boolean
93
+     */
94
+    protected function isValidField($meta, $field)
95
+    {
96
+        $mapping = $meta->getFieldMapping($field);
97
+        return $mapping && in_array($mapping['type'], $this->validTypes);
98
+    }
99
+}

+ 99 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php 查看文件

@@ -0,0 +1,99 @@
1
+<?php
2
+
3
+namespace Gedmo\Timestampable\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\File,
6
+    Gedmo\Mapping\Driver,
7
+    Gedmo\Exception\InvalidMappingException;
8
+
9
+/**
10
+ * This is a yaml mapping driver for Timestampable
11
+ * behavioral extension. Used for extraction of extended
12
+ * metadata from yaml specificaly for Timestampable
13
+ * extension.
14
+ *
15
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
16
+ * @package Gedmo.Timestampable.Mapping.Driver
17
+ * @subpackage Yaml
18
+ * @link http://www.gediminasm.org
19
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20
+ */
21
+class Yaml extends File implements Driver
22
+{
23
+    /**
24
+     * File extension
25
+     * @var string
26
+     */
27
+    protected $_extension = '.dcm.yml';
28
+
29
+    /**
30
+     * List of types which are valid for timestamp
31
+     *
32
+     * @var array
33
+     */
34
+    private $validTypes = array(
35
+        'date',
36
+        'time',
37
+        'datetime',
38
+        'datetimetz',
39
+        'timestamp',
40
+        'zenddate',
41
+        'vardatetime',
42
+        'integer'
43
+    );
44
+
45
+    /**
46
+     * {@inheritDoc}
47
+     */
48
+    public function readExtendedMetadata($meta, array &$config)
49
+    {
50
+        $mapping = $this->_getMapping($meta->name);
51
+
52
+        if (isset($mapping['fields'])) {
53
+            foreach ($mapping['fields'] as $field => $fieldMapping) {
54
+                if (isset($fieldMapping['gedmo']['timestampable'])) {
55
+                    $mappingProperty = $fieldMapping['gedmo']['timestampable'];
56
+                    if (!$this->isValidField($meta, $field)) {
57
+                        throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
58
+                    }
59
+                    if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
60
+                        throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
61
+                    }
62
+
63
+                    if ($mappingProperty['on'] == 'change') {
64
+                        if (!isset($mappingProperty['field'])) {
65
+                            throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
66
+                        }
67
+                        $field = array(
68
+                            'field' => $field,
69
+                            'trackedField' => $mappingProperty['field'],
70
+                            'value' => isset($mappingProperty['value']) ? $mappingProperty['value'] : null,
71
+                        );
72
+                    }
73
+                    $config[$mappingProperty['on']][] = $field;
74
+                }
75
+            }
76
+        }
77
+    }
78
+
79
+    /**
80
+     * {@inheritDoc}
81
+     */
82
+    protected function _loadMappingFile($file)
83
+    {
84
+        return \Symfony\Component\Yaml\Yaml::parse($file);
85
+    }
86
+
87
+    /**
88
+     * Checks if $field type is valid
89
+     *
90
+     * @param object $meta
91
+     * @param string $field
92
+     * @return boolean
93
+     */
94
+    protected function isValidField($meta, $field)
95
+    {
96
+        $mapping = $meta->getFieldMapping($field);
97
+        return $mapping && in_array($mapping['type'], $this->validTypes);
98
+    }
99
+}

+ 131 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php 查看文件

@@ -0,0 +1,131 @@
1
+<?php
2
+
3
+namespace Gedmo\Tool\Wrapper;
4
+
5
+use Doctrine\ORM\EntityManager;
6
+use Doctrine\ORM\Proxy\Proxy;
7
+
8
+/**
9
+ * Wraps entity or proxy for more convenient
10
+ * manipulation
11
+ *
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @package Gedmo.Tool.Wrapper
14
+ * @subpackage EntityWrapper
15
+ * @link http://www.gediminasm.org
16
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
17
+ */
18
+class EntityWrapper extends AbstractWrapper
19
+{
20
+    /**
21
+     * Entity identifier
22
+     *
23
+     * @var array
24
+     */
25
+    private $identifier;
26
+
27
+    /**
28
+     * True if entity or proxy is loaded
29
+     *
30
+     * @var boolean
31
+     */
32
+    private $initialized = false;
33
+
34
+    /**
35
+     * Wrapp entity
36
+     *
37
+     * @param object $entity
38
+     * @param \Doctrine\ORM\EntityManager $em
39
+     */
40
+    public function __construct($entity, EntityManager $em)
41
+    {
42
+        $this->om = $em;
43
+        $this->object = $entity;
44
+        $this->meta = $em->getClassMetadata(get_class($this->object));
45
+    }
46
+
47
+    /**
48
+     * {@inheritDoc}
49
+     */
50
+    public function getPropertyValue($property)
51
+    {
52
+        $this->initialize();
53
+        return $this->meta->getReflectionProperty($property)->getValue($this->object);
54
+    }
55
+
56
+    /**
57
+     * {@inheritDoc}
58
+     */
59
+    public function setPropertyValue($property, $value)
60
+    {
61
+        $this->initialize();
62
+        $this->meta->getReflectionProperty($property)->setValue($this->object, $value);
63
+        return $this;
64
+    }
65
+
66
+    /**
67
+     * {@inheritDoc}
68
+     */
69
+    public function hasValidIdentifier()
70
+    {
71
+        return (null !== $this->getIdentifier());
72
+    }
73
+
74
+    /**
75
+     * {@inheritDoc}
76
+     */
77
+    public function getRootObjectName()
78
+    {
79
+        return $this->meta->rootEntityName;
80
+    }
81
+
82
+    /**
83
+     * {@inheritDoc}
84
+     */
85
+    public function getIdentifier($single = true)
86
+    {
87
+        if (null === $this->identifier) {
88
+            if ($this->object instanceof Proxy) {
89
+                $uow = $this->om->getUnitOfWork();
90
+                if ($uow->isInIdentityMap($this->object)) {
91
+                    $this->identifier = $uow->getEntityIdentifier($this->object);
92
+                } else {
93
+                    $this->initialize();
94
+                }
95
+            }
96
+            if (null === $this->identifier) {
97
+                $this->identifier = array();
98
+                $incomplete = false;
99
+                foreach ($this->meta->identifier as $name) {
100
+                    $this->identifier[$name] = $this->getPropertyValue($name);
101
+                    if (null === $this->identifier[$name]) {
102
+                        $incomplete = true;
103
+                    }
104
+                }
105
+                if ($incomplete) {
106
+                    $this->identifier = null;
107
+                }
108
+            }
109
+        }
110
+        if ($single && is_array($this->identifier)) {
111
+            return reset($this->identifier);
112
+        }
113
+        return $this->identifier;
114
+    }
115
+
116
+    /**
117
+     * Initialize the entity if it is proxy
118
+     * required when is detached or not initialized
119
+     */
120
+    protected function initialize()
121
+    {
122
+        if (!$this->initialized) {
123
+            if ($this->object instanceof Proxy) {
124
+                $uow = $this->om->getUnitOfWork();
125
+                if (!$this->object->__isInitialized__) {
126
+                    $this->object->__load();
127
+                }
128
+            }
129
+        }
130
+    }
131
+}

+ 745 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php 查看文件

@@ -0,0 +1,745 @@
1
+<?php
2
+
3
+namespace Gedmo\Translatable;
4
+
5
+use Doctrine\Common\EventArgs;
6
+use Doctrine\ORM\ORMInvalidArgumentException;
7
+use Gedmo\Tool\Wrapper\AbstractWrapper;
8
+use Gedmo\Mapping\MappedEventSubscriber;
9
+use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
10
+use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
11
+use Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation;
12
+
13
+/**
14
+ * The translation listener handles the generation and
15
+ * loading of translations for entities which implements
16
+ * the Translatable interface.
17
+ *
18
+ * This behavior can impact the performance of your application
19
+ * since it does an additional query for each field to translate.
20
+ *
21
+ * Nevertheless the annotation metadata is properly cached and
22
+ * it is not a big overhead to lookup all entity annotations since
23
+ * the caching is activated for metadata
24
+ *
25
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
26
+ * @package Gedmo.Translatable
27
+ * @subpackage TranslatableListener
28
+ * @link http://www.gediminasm.org
29
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
30
+ */
31
+class TranslatableListener extends MappedEventSubscriber
32
+{
33
+    /**
34
+     * Query hint to override the fallback of translations
35
+     * integer 1 for true, 0 false
36
+     */
37
+    const HINT_FALLBACK = 'gedmo.translatable.fallback';
38
+
39
+    /**
40
+     * Query hint to override the fallback locale
41
+     */
42
+    const HINT_TRANSLATABLE_LOCALE = 'gedmo.translatable.locale';
43
+
44
+    /**
45
+     * Query hint to use inner join strategy for translations
46
+     */
47
+    const HINT_INNER_JOIN = 'gedmo.translatable.inner_join.translations';
48
+
49
+    /**
50
+     * Locale which is set on this listener.
51
+     * If Entity being translated has locale defined it
52
+     * will override this one
53
+     *
54
+     * @var string
55
+     */
56
+    protected $locale = 'en_US';
57
+
58
+    /**
59
+     * Default locale, this changes behavior
60
+     * to not update the original record field if locale
61
+     * which is used for updating is not default. This
62
+     * will load the default translation in other locales
63
+     * if record is not translated yet
64
+     *
65
+     * @var string
66
+     */
67
+    private $defaultLocale = 'en_US';
68
+
69
+    /**
70
+     * If this is set to false, when if entity does
71
+     * not have a translation for requested locale
72
+     * it will show a blank value
73
+     *
74
+     * @var boolean
75
+     */
76
+    private $translationFallback = false;
77
+
78
+    /**
79
+     * List of translations which do not have the foreign
80
+     * key generated yet - MySQL case. These translations
81
+     * will be updated with new keys on postPersist event
82
+     *
83
+     * @var array
84
+     */
85
+    private $pendingTranslationInserts = array();
86
+
87
+    /**
88
+     * Currently in case if there is TranslationQueryWalker
89
+     * in charge. We need to skip issuing additional queries
90
+     * on load
91
+     *
92
+     * @var boolean
93
+     */
94
+    private $skipOnLoad = false;
95
+
96
+    /**
97
+     * Tracks locale the objects currently translated in
98
+     *
99
+     * @var array
100
+     */
101
+    private $translatedInLocale = array();
102
+
103
+    /**
104
+     * Whether or not, to persist default locale
105
+     * translation or keep it in original record
106
+     *
107
+     * @var boolean
108
+     */
109
+    private $persistDefaultLocaleTranslation = false;
110
+
111
+    /**
112
+     * Tracks translation object for default locale
113
+     *
114
+     * @var array
115
+     */
116
+    private $translationInDefaultLocale = array();
117
+
118
+    /**
119
+     * Specifies the list of events to listen
120
+     *
121
+     * @return array
122
+     */
123
+    public function getSubscribedEvents()
124
+    {
125
+        return array(
126
+            'postLoad',
127
+            'postPersist',
128
+            'preFlush',
129
+            'onFlush',
130
+            'loadClassMetadata'
131
+        );
132
+    }
133
+
134
+    /**
135
+     * Set to skip or not onLoad event
136
+     *
137
+     * @param boolean $bool
138
+     * @return TranslatableListener
139
+     */
140
+    public function setSkipOnLoad($bool)
141
+    {
142
+        $this->skipOnLoad = (bool)$bool;
143
+        return $this;
144
+    }
145
+
146
+    /**
147
+     * Whether or not, to persist default locale
148
+     * translation or keep it in original record
149
+     *
150
+     * @param boolean $bool
151
+     * @return \Gedmo\Translatable\TranslatableListener
152
+     */
153
+    public function setPersistDefaultLocaleTranslation($bool)
154
+    {
155
+        $this->persistDefaultLocaleTranslation = (bool)$bool;
156
+        return $this;
157
+    }
158
+
159
+    /**
160
+     * Check if should persist default locale
161
+     * translation or keep it in original record
162
+     *
163
+     * @return boolean
164
+     */
165
+    public function getPersistDefaultLocaleTranslation()
166
+    {
167
+        return (bool)$this->persistDefaultLocaleTranslation;
168
+    }
169
+
170
+    /**
171
+     * Add additional $translation for pending $oid object
172
+     * which is being inserted
173
+     *
174
+     * @param string $oid
175
+     * @param object $translation
176
+     */
177
+    public function addPendingTranslationInsert($oid, $translation)
178
+    {
179
+        $this->pendingTranslationInserts[$oid][] = $translation;
180
+    }
181
+
182
+    /**
183
+     * Maps additional metadata
184
+     *
185
+     * @param EventArgs $eventArgs
186
+     * @return void
187
+     */
188
+    public function loadClassMetadata(EventArgs $eventArgs)
189
+    {
190
+        $ea = $this->getEventAdapter($eventArgs);
191
+        $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
192
+    }
193
+
194
+    /**
195
+     * Get the translation class to be used
196
+     * for the object $class
197
+     *
198
+     * @param TranslatableAdapter $ea
199
+     * @param string $class
200
+     * @return string
201
+     */
202
+    public function getTranslationClass(TranslatableAdapter $ea, $class)
203
+    {
204
+        return isset(self::$configurations[$this->name][$class]['translationClass']) ?
205
+            self::$configurations[$this->name][$class]['translationClass'] :
206
+            $ea->getDefaultTranslationClass()
207
+        ;
208
+    }
209
+
210
+    /**
211
+     * Enable or disable translation fallback
212
+     * to original record value
213
+     *
214
+     * @param boolean $bool
215
+     * @return TranslatableListener
216
+     */
217
+    public function setTranslationFallback($bool)
218
+    {
219
+        $this->translationFallback = (bool)$bool;
220
+        return $this;
221
+    }
222
+
223
+    /**
224
+     * Weather or not is using the translation
225
+     * fallback to original record
226
+     *
227
+     * @return boolean
228
+     */
229
+    public function getTranslationFallback()
230
+    {
231
+        return $this->translationFallback;
232
+    }
233
+
234
+    /**
235
+     * Set the locale to use for translation listener
236
+     *
237
+     * @param string $locale
238
+     * @return TranslatableListener
239
+     */
240
+    public function setTranslatableLocale($locale)
241
+    {
242
+        $this->validateLocale($locale);
243
+        $this->locale = $locale;
244
+        return $this;
245
+    }
246
+
247
+    /**
248
+     * Sets the default locale, this changes behavior
249
+     * to not update the original record field if locale
250
+     * which is used for updating is not default
251
+     *
252
+     * @param string $locale
253
+     * @return TranslatableListener
254
+     */
255
+    public function setDefaultLocale($locale)
256
+    {
257
+        $this->validateLocale($locale);
258
+        $this->defaultLocale = $locale;
259
+        return $this;
260
+    }
261
+
262
+    /**
263
+     * Gets the default locale
264
+     *
265
+     * @return string
266
+     */
267
+    public function getDefaultLocale()
268
+    {
269
+        return $this->defaultLocale;
270
+    }
271
+
272
+    /**
273
+     * Get currently set global locale, used
274
+     * extensively during query execution
275
+     *
276
+     * @return string
277
+     */
278
+    public function getListenerLocale()
279
+    {
280
+        return $this->locale;
281
+    }
282
+
283
+    /**
284
+     * Gets the locale to use for translation. Loads object
285
+     * defined locale first..
286
+     *
287
+     * @param object $object
288
+     * @param object $meta
289
+     * @throws RuntimeException - if language or locale property is not
290
+     *         found in entity
291
+     * @return string
292
+     */
293
+    public function getTranslatableLocale($object, $meta)
294
+    {
295
+        $locale = $this->locale;
296
+        if (isset(self::$configurations[$this->name][$meta->name]['locale'])) {
297
+            $class = $meta->getReflectionClass();
298
+            $reflectionProperty = $class->getProperty(self::$configurations[$this->name][$meta->name]['locale']);
299
+            if (!$reflectionProperty) {
300
+                $column = self::$configurations[$this->name][$meta->name]['locale'];
301
+                throw new \Gedmo\Exception\RuntimeException("There is no locale or language property ({$column}) found on object: {$meta->name}");
302
+            }
303
+            $reflectionProperty->setAccessible(true);
304
+            $value = $reflectionProperty->getValue($object);
305
+            try {
306
+                $this->validateLocale($value);
307
+                $locale = $value;
308
+            } catch(\Gedmo\Exception\InvalidArgumentException $e) {}
309
+        }
310
+
311
+        return $locale;
312
+    }
313
+
314
+    /**
315
+     * Handle translation changes in default locale
316
+     *
317
+     * This has to be done in the preFlush because, when an entity has been loaded
318
+     * in a different locale, no changes will be detected.
319
+     *
320
+     * @param EventArgs $args
321
+     * @return void
322
+     */
323
+    public function preFlush(EventArgs $args)
324
+    {
325
+        $ea = $this->getEventAdapter($args);
326
+        $om = $ea->getObjectManager();
327
+        $uow = $om->getUnitOfWork();
328
+
329
+        foreach ($this->translationInDefaultLocale as $oid => $fields) {
330
+            $trans = reset($fields);
331
+            if ($ea->usesPersonalTranslation(get_class($trans))) {
332
+                $entity = $trans->getObject();
333
+            } else {
334
+                $entity = $uow->tryGetById($trans->getForeignKey(), $trans->getObjectClass());
335
+            }
336
+
337
+            if (!$entity) {
338
+                continue;
339
+            }
340
+
341
+            try {
342
+                $uow->scheduleForUpdate($entity);
343
+            } catch (ORMInvalidArgumentException $e) {
344
+                foreach ($fields as $field => $trans) {
345
+                    $this->removeTranslationInDefaultLocale($oid, $field);
346
+                }
347
+            }
348
+        }
349
+    }
350
+
351
+    /**
352
+     * Looks for translatable objects being inserted or updated
353
+     * for further processing
354
+     *
355
+     * @param EventArgs $args
356
+     * @return void
357
+     */
358
+    public function onFlush(EventArgs $args)
359
+    {
360
+        $ea = $this->getEventAdapter($args);
361
+        $om = $ea->getObjectManager();
362
+        $uow = $om->getUnitOfWork();
363
+        // check all scheduled inserts for Translatable objects
364
+        foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
365
+            $meta = $om->getClassMetadata(get_class($object));
366
+            $config = $this->getConfiguration($om, $meta->name);
367
+            if (isset($config['fields'])) {
368
+                $this->handleTranslatableObjectUpdate($ea, $object, true);
369
+            }
370
+        }
371
+        // check all scheduled updates for Translatable entities
372
+        foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
373
+            $meta = $om->getClassMetadata(get_class($object));
374
+            $config = $this->getConfiguration($om, $meta->name);
375
+            if (isset($config['fields'])) {
376
+                $this->handleTranslatableObjectUpdate($ea, $object, false);
377
+            }
378
+        }
379
+        // check scheduled deletions for Translatable entities
380
+        foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
381
+            $meta = $om->getClassMetadata(get_class($object));
382
+            $config = $this->getConfiguration($om, $meta->name);
383
+            if (isset($config['fields'])) {
384
+                $wrapped = AbstractWrapper::wrap($object, $om);
385
+                $transClass = $this->getTranslationClass($ea, $meta->name);
386
+                $ea->removeAssociatedTranslations($wrapped, $transClass, $config['useObjectClass']);
387
+            }
388
+        }
389
+    }
390
+
391
+     /**
392
+     * Checks for inserted object to update their translation
393
+     * foreign keys
394
+     *
395
+     * @param EventArgs $args
396
+     * @return void
397
+     */
398
+    public function postPersist(EventArgs $args)
399
+    {
400
+        $ea = $this->getEventAdapter($args);
401
+        $om = $ea->getObjectManager();
402
+        $object = $ea->getObject();
403
+        $meta = $om->getClassMetadata(get_class($object));
404
+        // check if entity is tracked by translatable and without foreign key
405
+        if ($this->getConfiguration($om, $meta->name) && count($this->pendingTranslationInserts)) {
406
+            $oid = spl_object_hash($object);
407
+            if (array_key_exists($oid, $this->pendingTranslationInserts)) {
408
+                // load the pending translations without key
409
+                $wrapped = AbstractWrapper::wrap($object, $om);
410
+                $objectId = $wrapped->getIdentifier();
411
+                foreach ($this->pendingTranslationInserts[$oid] as $translation) {
412
+                    $translation->setForeignKey($objectId);
413
+                    $ea->insertTranslationRecord($translation);
414
+                }
415
+                unset($this->pendingTranslationInserts[$oid]);
416
+            }
417
+        }
418
+    }
419
+
420
+    /**
421
+     * After object is loaded, listener updates the translations
422
+     * by currently used locale
423
+     *
424
+     * @param EventArgs $args
425
+     * @return void
426
+     */
427
+    public function postLoad(EventArgs $args)
428
+    {
429
+        $ea = $this->getEventAdapter($args);
430
+        $om = $ea->getObjectManager();
431
+        $object = $ea->getObject();
432
+        $meta = $om->getClassMetadata(get_class($object));
433
+        $config = $this->getConfiguration($om, $meta->name);
434
+        if (isset($config['fields'])) {
435
+            $locale = $this->getTranslatableLocale($object, $meta);
436
+            $oid = spl_object_hash($object);
437
+            $this->translatedInLocale[$oid] = $locale;
438
+        }
439
+
440
+        if ($this->skipOnLoad) {
441
+            return;
442
+        }
443
+
444
+        if (isset($config['fields']) && $locale !== $this->defaultLocale) {
445
+            // fetch translations
446
+            $translationClass = $this->getTranslationClass($ea, $config['useObjectClass']);
447
+            $result = $ea->loadTranslations(
448
+                $object,
449
+                $translationClass,
450
+                $locale,
451
+                $config['useObjectClass']
452
+            );
453
+            // translate object's translatable properties
454
+            foreach ($config['fields'] as $field) {
455
+                $translated = '';
456
+                foreach ((array)$result as $entry) {
457
+                    if ($entry['field'] == $field) {
458
+                        $translated = $entry['content'];
459
+                        break;
460
+                    }
461
+                }
462
+                // update translation
463
+                if ($translated
464
+                    || (!$this->translationFallback && (!isset($config['fallback'][$field]) || !$config['fallback'][$field]))
465
+                    || ($this->translationFallback && isset($config['fallback'][$field]) && !$config['fallback'][$field])
466
+                ) {
467
+                    $ea->setTranslationValue($object, $field, $translated);
468
+                    // ensure clean changeset
469
+                    $ea->setOriginalObjectProperty(
470
+                        $om->getUnitOfWork(),
471
+                        $oid,
472
+                        $field,
473
+                        $meta->getReflectionProperty($field)->getValue($object)
474
+                    );
475
+                }
476
+            }
477
+        }
478
+    }
479
+
480
+    /**
481
+     * {@inheritDoc}
482
+     */
483
+    protected function getNamespace()
484
+    {
485
+        return __NAMESPACE__;
486
+    }
487
+
488
+    /**
489
+     * Validates the given locale
490
+     *
491
+     * @param string $locale - locale to validate
492
+     * @throws InvalidArgumentException if locale is not valid
493
+     * @return void
494
+     */
495
+    protected function validateLocale($locale)
496
+    {
497
+        if (!is_string($locale) || !strlen($locale)) {
498
+            throw new \Gedmo\Exception\InvalidArgumentException('Locale or language cannot be empty and must be set through Listener or Entity');
499
+        }
500
+    }
501
+
502
+    /**
503
+     * Creates the translation for object being flushed
504
+     *
505
+     * @param TranslatableAdapter $ea
506
+     * @param object $object
507
+     * @param boolean $isInsert
508
+     * @throws UnexpectedValueException - if locale is not valid, or
509
+     *      primary key is composite, missing or invalid
510
+     * @return void
511
+     */
512
+    private function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert)
513
+    {
514
+        $om = $ea->getObjectManager();
515
+        $wrapped = AbstractWrapper::wrap($object, $om);
516
+        $meta = $wrapped->getMetadata();
517
+        $config = $this->getConfiguration($om, $meta->name);
518
+        // no need cache, metadata is loaded only once in MetadataFactoryClass
519
+        $translationClass = $this->getTranslationClass($ea, $config['useObjectClass']);
520
+        $translationMetadata = $om->getClassMetadata($translationClass);
521
+
522
+        // check for the availability of the primary key
523
+        $objectId = $wrapped->getIdentifier();
524
+        // load the currently used locale
525
+        $locale = $this->getTranslatableLocale($object, $meta);
526
+
527
+        $uow = $om->getUnitOfWork();
528
+        $oid = spl_object_hash($object);
529
+        $changeSet = $ea->getObjectChangeSet($uow, $object);
530
+        $translatableFields = $config['fields'];
531
+        foreach ($translatableFields as $field) {
532
+            $wasPersistedSeparetely = false;
533
+            $skip = isset($this->translatedInLocale[$oid]) && $locale === $this->translatedInLocale[$oid];
534
+            $skip = $skip && !isset($changeSet[$field]) && !$this->getTranslationInDefaultLocale($oid, $field);
535
+            if ($skip) {
536
+                continue; // locale is same and nothing changed
537
+            }
538
+            $translation = null;
539
+            foreach ($ea->getScheduledObjectInsertions($uow) as $trans) {
540
+                if ($locale !== $this->defaultLocale
541
+                    && get_class($trans) === $translationClass
542
+                    && $trans->getLocale() === $this->defaultLocale
543
+                    && $trans->getField() === $field
544
+                    && $this->belongsToObject($ea, $trans, $object)) {
545
+                    $this->setTranslationInDefaultLocale($oid, $field, $trans);
546
+                    break;
547
+                }
548
+            }
549
+            // lookup persisted translations
550
+            if ($ea->usesPersonalTranslation($translationClass)) {
551
+                foreach ($ea->getScheduledObjectInsertions($uow) as $trans) {
552
+                    $wasPersistedSeparetely = get_class($trans) === $translationClass
553
+                        && $trans->getLocale() === $locale
554
+                        && $trans->getField() === $field
555
+                        && $trans->getObject() === $object
556
+                    ;
557
+                    if ($wasPersistedSeparetely) {
558
+                        $translation = $trans;
559
+                        break;
560
+                    }
561
+                }
562
+            }
563
+            // check if translation allready is created
564
+            if (!$isInsert && !$translation) {
565
+                $translation = $ea->findTranslation(
566
+                    $wrapped,
567
+                    $locale,
568
+                    $field,
569
+                    $translationClass,
570
+                    $config['useObjectClass']
571
+                );
572
+            }
573
+
574
+            // create new translation if translation not already created and locale is different from default locale, otherwise, we have the date in the original record
575
+            $persistNewTranslation = !$translation
576
+                && ($locale !== $this->defaultLocale || $this->persistDefaultLocaleTranslation)
577
+            ;
578
+            if ($persistNewTranslation) {
579
+                $translation = $translationMetadata->newInstance();
580
+                $translation->setLocale($locale);
581
+                $translation->setField($field);
582
+                if ($ea->usesPersonalTranslation($translationClass)) {
583
+                    $translation->setObject($object);
584
+                } else {
585
+                    $translation->setObjectClass($config['useObjectClass']);
586
+                    $translation->setForeignKey($objectId);
587
+                }
588
+            }
589
+
590
+            if ($translation) {
591
+                // set the translated field, take value using reflection
592
+                $content = $ea->getTranslationValue($object, $field);
593
+                $translation->setContent($content);
594
+                // check if need to update in database
595
+                $transWrapper = AbstractWrapper::wrap($translation, $om);
596
+                if ((is_bool($content) || is_int($content) || (is_string($content) && strlen($content) > 0) || !empty($content)) && ($isInsert || !$transWrapper->getIdentifier() || isset($changeSet[$field]))) {
597
+                    if ($isInsert && !$objectId && !$ea->usesPersonalTranslation($translationClass)) {
598
+                        // if we do not have the primary key yet available
599
+                        // keep this translation in memory to insert it later with foreign key
600
+                        $this->pendingTranslationInserts[spl_object_hash($object)][] = $translation;
601
+                    } else {
602
+                        // persist and compute change set for translation
603
+                        if ($wasPersistedSeparetely) {
604
+                            $ea->recomputeSingleObjectChangeset($uow, $translationMetadata, $translation);
605
+                        } else {
606
+                            $om->persist($translation);
607
+                            $uow->computeChangeSet($translationMetadata, $translation);
608
+                        }
609
+                    }
610
+                }
611
+            }
612
+
613
+            if ($isInsert && $this->getTranslationInDefaultLocale($oid, $field) !== null) {
614
+                // We can't rely on object field value which is created in non-default locale.
615
+                // If we provide translation for default locale as well, the latter is considered to be trusted
616
+                // and object content should be overridden.
617
+                $wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent());
618
+                $ea->recomputeSingleObjectChangeset($uow, $meta, $object);
619
+                $this->removeTranslationInDefaultLocale($oid, $field);
620
+            }
621
+        }
622
+        $this->translatedInLocale[$oid] = $locale;
623
+        // check if we have default translation and need to reset the translation
624
+        if (!$isInsert && strlen($this->defaultLocale)) {
625
+            $this->validateLocale($this->defaultLocale);
626
+            $modifiedChangeSet = $changeSet;
627
+            foreach ($changeSet as $field => $changes) {
628
+                if (in_array($field, $translatableFields)) {
629
+                    if ($locale !== $this->defaultLocale) {
630
+                        $ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]);
631
+                        unset($modifiedChangeSet[$field]);
632
+                    }
633
+                }
634
+            }
635
+            $ea->recomputeSingleObjectChangeset($uow, $meta, $object);
636
+            // cleanup current changeset only if working in a another locale different than de default one, otherwise the changeset will always be reverted
637
+            if ($locale !== $this->defaultLocale) {
638
+                $ea->clearObjectChangeSet($uow, $oid);
639
+                // recompute changeset only if there are changes other than reverted translations
640
+                if ($modifiedChangeSet || $this->hasTranslationsInDefaultLocale($oid)) {
641
+                    foreach ($modifiedChangeSet as $field => $changes) {
642
+                        $ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]);
643
+                    }
644
+                    foreach ($translatableFields as $field) {
645
+                        if ($this->getTranslationInDefaultLocale($oid, $field) !== null) {
646
+                            $wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent());
647
+                            $this->removeTranslationInDefaultLocale($oid, $field);
648
+                        }
649
+                    }
650
+                    $ea->recomputeSingleObjectChangeset($uow, $meta, $object);
651
+                }
652
+            }
653
+        }
654
+    }
655
+
656
+    /**
657
+     * Sets translation object which represents translation in default language.
658
+     *
659
+     * @param    string    $oid     hash of basic entity
660
+     * @param    string    $field   field of basic entity
661
+     * @param    mixed     $trans   Translation object
662
+     */
663
+    public function setTranslationInDefaultLocale($oid, $field, $trans)
664
+    {
665
+        if (!isset($this->translationInDefaultLocale[$oid])) {
666
+            $this->translationInDefaultLocale[$oid] = array();
667
+        }
668
+        $this->translationInDefaultLocale[$oid][$field] = $trans;
669
+    }
670
+
671
+    /**
672
+     * Removes translation object which represents translation in default language.
673
+     * This is for internal use only.
674
+     *
675
+     * @param string    $oid     hash of the basic entity
676
+     * @param string    $field   field of basic entity
677
+     */
678
+    private function removeTranslationInDefaultLocale($oid, $field)
679
+    {
680
+        if (isset($this->translationInDefaultLocale[$oid])) {
681
+            if (isset($this->translationInDefaultLocale[$oid][$field])) {
682
+                unset($this->translationInDefaultLocale[$oid][$field]);
683
+            }
684
+            if (! $this->translationInDefaultLocale[$oid]) {
685
+                // We removed the final remaining elements from the
686
+                // translationInDefaultLocale[$oid] array, so we might as well
687
+                // completely remove the entry at $oid.
688
+                unset($this->translationInDefaultLocale[$oid]);
689
+            }
690
+        }
691
+    }
692
+
693
+    /**
694
+     * Gets translation object which represents translation in default language.
695
+     * This is for internal use only.
696
+     *
697
+     * @param    string    $oid   hash of the basic entity
698
+     * @param    string    $field field of basic entity
699
+     * @return   mixed     Returns translation object if it exists or NULL otherwise
700
+     */
701
+    private function getTranslationInDefaultLocale($oid, $field)
702
+    {
703
+        if (array_key_exists($oid, $this->translationInDefaultLocale)) {
704
+            if (array_key_exists($field, $this->translationInDefaultLocale[$oid])) {
705
+                $ret = $this->translationInDefaultLocale[$oid][$field];
706
+            } else {
707
+                $ret = null;
708
+            }
709
+        } else {
710
+            $ret = null;
711
+        }
712
+        return $ret;
713
+    }
714
+
715
+    /**
716
+     * Check if object has any translation object which represents translation in default language.
717
+     * This is for internal use only.
718
+     *
719
+     * @param    string    $oid   hash of the basic entity
720
+     * @return   bool
721
+     */
722
+    public function hasTranslationsInDefaultLocale($oid)
723
+    {
724
+        return array_key_exists($oid, $this->translationInDefaultLocale);
725
+    }
726
+     
727
+    /**
728
+     * Checks if the translation entity belongs to the object in question
729
+     *
730
+     * @param   TranslatableAdapter $ea
731
+     * @param   mixed               $trans
732
+     * @param   mixed               $object
733
+     * @return  boolean
734
+     */
735
+    private function belongsToObject(TranslatableAdapter $ea, $trans, $object)
736
+    {
737
+        if ($ea->usesPersonalTranslation(get_class($trans))) {
738
+            return $trans->getObject() === $object;
739
+        }
740
+
741
+        return ($trans->getForeignKey() === $object->getId()
742
+            && ($trans->getObjectClass() === get_class($object)));
743
+    }
744
+}
745
+

+ 250 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php 查看文件

@@ -0,0 +1,250 @@
1
+<?php
2
+
3
+namespace Gedmo\Tree\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\AbstractAnnotationDriver,
6
+    Gedmo\Exception\InvalidMappingException,
7
+    Gedmo\Tree\Mapping\Validator;
8
+
9
+/**
10
+ * This is an annotation mapping driver for Tree
11
+ * behavioral extension. Used for extraction of extended
12
+ * metadata from Annotations specificaly for Tree
13
+ * extension.
14
+ *
15
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
16
+ * @author <rocco@roccosportal.com>
17
+ * @package Gedmo.Tree.Mapping.Driver
18
+ * @subpackage Annotation
19
+ * @link http://www.gediminasm.org
20
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21
+ */
22
+class Annotation extends AbstractAnnotationDriver
23
+{
24
+    /**
25
+     * Annotation to define the tree type
26
+     */
27
+    const TREE = 'Gedmo\\Mapping\\Annotation\\Tree';
28
+
29
+    /**
30
+     * Annotation to mark field as one which will store left value
31
+     */
32
+    const LEFT = 'Gedmo\\Mapping\\Annotation\\TreeLeft';
33
+
34
+    /**
35
+     * Annotation to mark field as one which will store right value
36
+     */
37
+    const RIGHT = 'Gedmo\\Mapping\\Annotation\\TreeRight';
38
+
39
+    /**
40
+     * Annotation to mark relative parent field
41
+     */
42
+    const PARENT = 'Gedmo\\Mapping\\Annotation\\TreeParent';
43
+
44
+    /**
45
+     * Annotation to mark node level
46
+     */
47
+    const LEVEL = 'Gedmo\\Mapping\\Annotation\\TreeLevel';
48
+
49
+    /**
50
+     * Annotation to mark field as tree root
51
+     */
52
+    const ROOT = 'Gedmo\\Mapping\\Annotation\\TreeRoot';
53
+
54
+    /**
55
+     * Annotation to specify closure tree class
56
+     */
57
+    const CLOSURE = 'Gedmo\\Mapping\\Annotation\\TreeClosure';
58
+
59
+    /**
60
+     * Annotation to specify path class
61
+     */
62
+    const PATH = 'Gedmo\\Mapping\\Annotation\\TreePath';
63
+
64
+    /**
65
+     * Annotation to specify path source class
66
+     */
67
+    const PATH_SOURCE = 'Gedmo\\Mapping\\Annotation\\TreePathSource';
68
+
69
+    /**
70
+     * Annotation to specify path hash class
71
+     */
72
+    const PATH_HASH = 'Gedmo\\Mapping\\Annotation\\TreePathHash';
73
+
74
+    /**
75
+     * Annotation to mark the field to be used to hold the lock time
76
+     */
77
+    const LOCK_TIME = 'Gedmo\\Mapping\\Annotation\\TreeLockTime';
78
+
79
+    /**
80
+     * List of tree strategies available
81
+     *
82
+     * @var array
83
+     */
84
+    protected $strategies = array(
85
+        'nested',
86
+        'closure',
87
+        'materializedPath'
88
+    );
89
+
90
+    /**
91
+     * {@inheritDoc}
92
+     */
93
+    public function readExtendedMetadata($meta, array &$config)
94
+    {
95
+        $validator = new Validator();
96
+        $class = $this->getMetaReflectionClass($meta);
97
+        // class annotations
98
+        if ($annot = $this->reader->getClassAnnotation($class, self::TREE)) {
99
+            if (!in_array($annot->type, $this->strategies)) {
100
+                throw new InvalidMappingException("Tree type: {$annot->type} is not available.");
101
+            }
102
+            $config['strategy'] = $annot->type;
103
+            $config['activate_locking'] = $annot->activateLocking;
104
+            $config['locking_timeout'] = (int) $annot->lockingTimeout;
105
+
106
+            if ($config['locking_timeout'] < 1) {
107
+                throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second.");
108
+            }
109
+        }
110
+        if ($annot = $this->reader->getClassAnnotation($class, self::CLOSURE)) {
111
+            if (!class_exists($annot->class)) {
112
+                throw new InvalidMappingException("Tree closure class: {$annot->class} does not exist.");
113
+            }
114
+            $config['closure'] = $annot->class;
115
+        }
116
+
117
+        // property annotations
118
+        foreach ($class->getProperties() as $property) {
119
+            if ($meta->isMappedSuperclass && !$property->isPrivate() ||
120
+                $meta->isInheritedField($property->name) ||
121
+                isset($meta->associationMappings[$property->name]['inherited'])
122
+            ) {
123
+                continue;
124
+            }
125
+            // left
126
+            if ($this->reader->getPropertyAnnotation($property, self::LEFT)) {
127
+                $field = $property->getName();
128
+                if (!$meta->hasField($field)) {
129
+                    throw new InvalidMappingException("Unable to find 'left' - [{$field}] as mapped property in entity - {$meta->name}");
130
+                }
131
+                if (!$validator->isValidField($meta, $field)) {
132
+                    throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
133
+                }
134
+                $config['left'] = $field;
135
+            }
136
+            // right
137
+            if ($this->reader->getPropertyAnnotation($property, self::RIGHT)) {
138
+                $field = $property->getName();
139
+                if (!$meta->hasField($field)) {
140
+                    throw new InvalidMappingException("Unable to find 'right' - [{$field}] as mapped property in entity - {$meta->name}");
141
+                }
142
+                if (!$validator->isValidField($meta, $field)) {
143
+                    throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
144
+                }
145
+                $config['right'] = $field;
146
+            }
147
+            // ancestor/parent
148
+            if ($this->reader->getPropertyAnnotation($property, self::PARENT)) {
149
+                $field = $property->getName();
150
+                if (!$meta->isSingleValuedAssociation($field)) {
151
+                    throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}");
152
+                }
153
+                $config['parent'] = $field;
154
+            }
155
+            // root
156
+            if ($this->reader->getPropertyAnnotation($property, self::ROOT)) {
157
+                $field = $property->getName();
158
+                if (!$meta->hasField($field)) {
159
+                    throw new InvalidMappingException("Unable to find 'root' - [{$field}] as mapped property in entity - {$meta->name}");
160
+                }
161
+
162
+                if (!$validator->isValidFieldForRoot($meta, $field)) {
163
+                    throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be any of the 'integer' types or 'string' in class - {$meta->name}");
164
+                }
165
+                $config['root'] = $field;
166
+            }
167
+            // level
168
+            if ($this->reader->getPropertyAnnotation($property, self::LEVEL)) {
169
+                $field = $property->getName();
170
+                if (!$meta->hasField($field)) {
171
+                    throw new InvalidMappingException("Unable to find 'level' - [{$field}] as mapped property in entity - {$meta->name}");
172
+                }
173
+                if (!$validator->isValidField($meta, $field)) {
174
+                    throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
175
+                }
176
+                $config['level'] = $field;
177
+            }
178
+            // path
179
+            if ($pathAnnotation = $this->reader->getPropertyAnnotation($property, self::PATH)) {
180
+                $field = $property->getName();
181
+                if (!$meta->hasField($field)) {
182
+                    throw new InvalidMappingException("Unable to find 'path' - [{$field}] as mapped property in entity - {$meta->name}");
183
+                }
184
+                if (!$validator->isValidFieldForPath($meta, $field)) {
185
+                    throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}");
186
+                }
187
+                if (strlen($pathAnnotation->separator) > 1) {
188
+                    throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$pathAnnotation->separator} is invalid. It must be only one character long.");
189
+                }
190
+                $config['path'] = $field;
191
+                $config['path_separator'] = $pathAnnotation->separator;
192
+                $config['path_append_id'] = $pathAnnotation->appendId;
193
+                $config['path_starts_with_separator'] = $pathAnnotation->startsWithSeparator;
194
+                $config['path_ends_with_separator'] = $pathAnnotation->endsWithSeparator;
195
+            }
196
+            // path source
197
+            if ($this->reader->getPropertyAnnotation($property, self::PATH_SOURCE)) {
198
+                $field = $property->getName();
199
+                if (!$meta->hasField($field)) {
200
+                    throw new InvalidMappingException("Unable to find 'path_source' - [{$field}] as mapped property in entity - {$meta->name}");
201
+                }
202
+                if (!$validator->isValidFieldForPathSource($meta, $field)) {
203
+                    throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}");
204
+                }
205
+                $config['path_source'] = $field;
206
+            }
207
+
208
+             // path hash
209
+            if ($this->reader->getPropertyAnnotation($property, self::PATH_HASH)) {
210
+                $field = $property->getName();
211
+                if (!$meta->hasField($field)) {
212
+                    throw new InvalidMappingException("Unable to find 'path_hash' - [{$field}] as mapped property in entity - {$meta->name}");
213
+                }
214
+                if (!$validator->isValidFieldForPathHash($meta, $field)) {
215
+                    throw new InvalidMappingException("Tree PathHash field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}");
216
+                }
217
+                $config['path_hash'] = $field;
218
+
219
+            }
220
+            // lock time
221
+
222
+            if ($this->reader->getPropertyAnnotation($property, self::LOCK_TIME)) {
223
+                $field = $property->getName();
224
+                if (!$meta->hasField($field)) {
225
+                    throw new InvalidMappingException("Unable to find 'lock_time' - [{$field}] as mapped property in entity - {$meta->name}");
226
+                }
227
+                if (!$validator->isValidFieldForLockTime($meta, $field)) {
228
+                    throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}");
229
+                }
230
+                $config['lock_time'] = $field;
231
+            }
232
+        }
233
+
234
+        if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) {
235
+            throw new InvalidMappingException("You need to map a date field as the tree lock time field to activate locking support.");
236
+        }
237
+
238
+        if (!$meta->isMappedSuperclass && $config) {
239
+            if (isset($config['strategy'])) {
240
+                if (is_array($meta->identifier) && count($meta->identifier) > 1) {
241
+                    throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}");
242
+                }
243
+                $method = 'validate' . ucfirst($config['strategy']) . 'TreeMetadata';
244
+                $validator->$method($meta, $config);
245
+            } else {
246
+                throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}");
247
+            }
248
+        }
249
+    }
250
+}

+ 197 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php 查看文件

@@ -0,0 +1,197 @@
1
+<?php
2
+
3
+namespace Gedmo\Tree\Mapping\Driver;
4
+
5
+use Gedmo\Mapping\Driver\File,
6
+    Gedmo\Mapping\Driver,
7
+    Gedmo\Exception\InvalidMappingException,
8
+    Gedmo\Tree\Mapping\Validator;
9
+
10
+/**
11
+ * This is a yaml mapping driver for Tree
12
+ * behavioral extension. Used for extraction of extended
13
+ * metadata from yaml specificaly for Tree
14
+ * extension.
15
+ *
16
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
17
+ * @package Gedmo.Tree.Mapping.Driver
18
+ * @subpackage Yaml
19
+ * @link http://www.gediminasm.org
20
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21
+ */
22
+class Yaml extends File implements Driver
23
+{
24
+    /**
25
+     * File extension
26
+     * @var string
27
+     */
28
+    protected $_extension = '.dcm.yml';
29
+
30
+    /**
31
+     * List of tree strategies available
32
+     *
33
+     * @var array
34
+     */
35
+    private $strategies = array(
36
+        'nested',
37
+        'closure',
38
+        'materializedPath'
39
+    );
40
+
41
+    /**
42
+     * {@inheritDoc}
43
+     */
44
+    public function readExtendedMetadata($meta, array &$config)
45
+    {
46
+        $mapping = $this->_getMapping($meta->name);
47
+        $validator = new Validator();
48
+
49
+        if (isset($mapping['gedmo'])) {
50
+            $classMapping = $mapping['gedmo'];
51
+            if (isset($classMapping['tree']['type'])) {
52
+                $strategy = $classMapping['tree']['type'];
53
+                if (!in_array($strategy, $this->strategies)) {
54
+                    throw new InvalidMappingException("Tree type: $strategy is not available.");
55
+                }
56
+                $config['strategy'] = $strategy;
57
+                $config['activate_locking'] = isset($classMapping['tree']['activateLocking']) ?
58
+                    $classMapping['tree']['activateLocking'] : false;
59
+                $config['locking_timeout'] = isset($classMapping['tree']['lockingTimeout']) ?
60
+                    (int) $classMapping['tree']['lockingTimeout'] : 3;
61
+
62
+                if ($config['locking_timeout'] < 1) {
63
+                    throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second.");
64
+                }
65
+            }
66
+            if (isset($classMapping['tree']['closure'])) {
67
+                $class = $classMapping['tree']['closure'];
68
+                if (!class_exists($class)) {
69
+                    throw new InvalidMappingException("Tree closure class: {$class} does not exist.");
70
+                }
71
+                $config['closure'] = $class;
72
+            }
73
+        }
74
+        if (isset($mapping['fields'])) {
75
+            foreach ($mapping['fields'] as $field => $fieldMapping) {
76
+                if (isset($fieldMapping['gedmo'])) {
77
+                    if (in_array('treeLeft', $fieldMapping['gedmo'])) {
78
+                        if (!$validator->isValidField($meta, $field)) {
79
+                            throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
80
+                        }
81
+                        $config['left'] = $field;
82
+                    } elseif (in_array('treeRight', $fieldMapping['gedmo'])) {
83
+                        if (!$validator->isValidField($meta, $field)) {
84
+                            throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
85
+                        }
86
+                        $config['right'] = $field;
87
+                    } elseif (in_array('treeLevel', $fieldMapping['gedmo'])) {
88
+                        if (!$validator->isValidField($meta, $field)) {
89
+                            throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
90
+                        }
91
+                        $config['level'] = $field;
92
+                    } elseif (in_array('treeRoot', $fieldMapping['gedmo'])) {
93
+                        if (!$validator->isValidFieldForRoot($meta, $field)) {
94
+                            throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be any of the 'integer' types or 'string' in class - {$meta->name}");
95
+                        }
96
+                        $config['root'] = $field;
97
+                    } elseif (in_array('treePath', $fieldMapping['gedmo']) || isset($fieldMapping['gedmo']['treePath'])) {
98
+                        if (!$validator->isValidFieldForPath($meta, $field)) {
99
+                            throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}");
100
+                        }
101
+
102
+                        $treePathInfo = isset($fieldMapping['gedmo']['treePath']) ? $fieldMapping['gedmo']['treePath'] :
103
+                            $fieldMapping['gedmo'][array_search('treePath', $fieldMapping['gedmo'])];
104
+
105
+                        if (is_array($treePathInfo) && isset($treePathInfo['separator'])) {
106
+                            $separator = $treePathInfo['separator'];
107
+                        } else {
108
+                            $separator = '|';
109
+                        }
110
+
111
+                        if (strlen($separator) > 1) {
112
+                            throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$separator} is invalid. It must be only one character long.");
113
+                        }
114
+
115
+                        if (is_array($treePathInfo) && isset($treePathInfo['appendId'])) {
116
+                            $appendId = $treePathInfo['appendId'];
117
+                        } else {
118
+                            $appendId = null;
119
+                        }
120
+
121
+                        if (is_array($treePathInfo) && isset($treePathInfo['startsWithSeparator'])) {
122
+                            $startsWithSeparator = $treePathInfo['startsWithSeparator'];
123
+                        } else {
124
+                            $startsWithSeparator = false;
125
+                        }
126
+
127
+                        if (is_array($treePathInfo) && isset($treePathInfo['endsWithSeparator'])) {
128
+                            $endsWithSeparator = $treePathInfo['endsWithSeparator'];
129
+                        } else {
130
+                            $endsWithSeparator = true;
131
+                        }
132
+
133
+                        $config['path'] = $field;
134
+                        $config['path_separator'] = $separator;
135
+                        $config['path_append_id'] = $appendId;
136
+                        $config['path_starts_with_separator'] = $startsWithSeparator;
137
+                        $config['path_ends_with_separator'] = $endsWithSeparator;
138
+                    } elseif (in_array('treePathSource', $fieldMapping['gedmo'])) {
139
+                        if (!$validator->isValidFieldForPathSource($meta, $field)) {
140
+                            throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}");
141
+                        }
142
+                        $config['path_source'] = $field;
143
+                    } elseif (in_array('treePathHash', $fieldMapping['gedmo'])) {
144
+                        if (!$validator->isValidFieldForPathSource($meta, $field)) {
145
+                            throw new InvalidMappingException("Tree PathHash field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}");
146
+                        }
147
+                        $config['path_hash'] = $field;
148
+                    } elseif (in_array('treeLockTime', $fieldMapping['gedmo'])) {
149
+                        if (!$validator->isValidFieldForLocktime($meta, $field)) {
150
+                            throw new InvalidMappingException("Tree LockTime field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}");
151
+                        }
152
+                        $config['lock_time'] = $field;
153
+                    } elseif (in_array('treeParent', $fieldMapping['gedmo'])) {
154
+                        $config['parent'] = $field;
155
+                    }
156
+                }
157
+            }
158
+        }
159
+
160
+        if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) {
161
+            throw new InvalidMappingException("You need to map a date|datetime|timestamp field as the tree lock time field to activate locking support.");
162
+        }
163
+
164
+        if (isset($mapping['manyToOne'])) {
165
+            foreach ($mapping['manyToOne'] as $field => $relationMapping) {
166
+                if (isset($relationMapping['gedmo'])) {
167
+                    if (in_array('treeParent', $relationMapping['gedmo'])) {
168
+                        if ($relationMapping['targetEntity'] != $meta->name) {
169
+                            throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}");
170
+                        }
171
+                        $config['parent'] = $field;
172
+                    }
173
+                }
174
+            }
175
+        }
176
+
177
+        if (!$meta->isMappedSuperclass && $config) {
178
+            if (isset($config['strategy'])) {
179
+                if (is_array($meta->identifier) && count($meta->identifier) > 1) {
180
+                    throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}");
181
+                }
182
+                $method = 'validate' . ucfirst($config['strategy']) . 'TreeMetadata';
183
+                $validator->$method($meta, $config);
184
+            } else {
185
+                throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}");
186
+            }
187
+        }
188
+    }
189
+
190
+    /**
191
+     * {@inheritDoc}
192
+     */
193
+    protected function _loadMappingFile($file)
194
+    {
195
+        return \Symfony\Component\Yaml\Yaml::parse($file);
196
+    }
197
+}

+ 230 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php 查看文件

@@ -0,0 +1,230 @@
1
+<?php
2
+
3
+namespace Gedmo\Tree\Mapping;
4
+
5
+use Gedmo\Exception\InvalidMappingException;
6
+
7
+/**
8
+ * This is a validator for all mapping drivers for Tree
9
+ * behavioral extension, containing methods to validate
10
+ * mapping information
11
+ *
12
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
13
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
14
+ * @author <rocco@roccosportal.com>
15
+ * @package Gedmo.Tree.Mapping
16
+ * @subpackage Validator
17
+ * @link http://www.gediminasm.org
18
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19
+ */
20
+class Validator
21
+{
22
+    /**
23
+     * List of types which are valid for tree fields
24
+     *
25
+     * @var array
26
+     */
27
+    private $validTypes = array(
28
+        'integer',
29
+        'smallint',
30
+        'bigint',
31
+        'int'
32
+    );
33
+
34
+    /**
35
+     * List of types which are valid for the path (materialized path strategy)
36
+     *
37
+     * @var array
38
+     */
39
+    private $validPathTypes = array(
40
+        'string',
41
+        'text'
42
+    );
43
+
44
+    /**
45
+     * List of types which are valid for the path source (materialized path strategy)
46
+     *
47
+     * @var array
48
+     */
49
+    private $validPathSourceTypes = array(
50
+        'id',
51
+        'integer',
52
+        'smallint',
53
+        'bigint',
54
+        'string',
55
+        'int',
56
+        'float'
57
+    );
58
+
59
+    /**
60
+     * List of types which are valid for the path hash (materialized path strategy)
61
+     *
62
+     * @var array
63
+     */
64
+    private $validPathHashTypes = array(
65
+        'string'
66
+    );
67
+
68
+    /**
69
+     * List of types which are valid for the path source (materialized path strategy)
70
+     *
71
+     * @var array
72
+     */
73
+    private $validRootTypes = array(
74
+        'integer',
75
+        'smallint',
76
+        'bigint',
77
+        'int',
78
+        'string'
79
+    );
80
+
81
+    /**
82
+     * Checks if $field type is valid
83
+     *
84
+     * @param object $meta
85
+     * @param string $field
86
+     * @return boolean
87
+     */
88
+    public function isValidField($meta, $field)
89
+    {
90
+        $mapping = $meta->getFieldMapping($field);
91
+        return $mapping && in_array($mapping['type'], $this->validTypes);
92
+    }
93
+
94
+    /**
95
+     * Checks if $field type is valid for Path field
96
+     *
97
+     * @param object $meta
98
+     * @param string $field
99
+     * @return boolean
100
+     */
101
+    public function isValidFieldForPath($meta, $field)
102
+    {
103
+        $mapping = $meta->getFieldMapping($field);
104
+        return $mapping && in_array($mapping['type'], $this->validPathTypes);
105
+    }
106
+
107
+    /**
108
+     * Checks if $field type is valid for PathSource field
109
+     *
110
+     * @param object $meta
111
+     * @param string $field
112
+     * @return boolean
113
+     */
114
+    public function isValidFieldForPathSource($meta, $field)
115
+    {
116
+        $mapping = $meta->getFieldMapping($field);
117
+        return $mapping && in_array($mapping['type'], $this->validPathSourceTypes);
118
+    }
119
+
120
+    /**
121
+     * Checks if $field type is valid for PathHash field
122
+     *
123
+     * @param object $meta
124
+     * @param string $field
125
+     * @return boolean
126
+     */
127
+    public function isValidFieldForPathHash($meta, $field)
128
+    {
129
+        $mapping = $meta->getFieldMapping($field);
130
+        return $mapping && in_array($mapping['type'], $this->validPathHashTypes);
131
+    }
132
+
133
+    /**
134
+     * Checks if $field type is valid for LockTime field
135
+     *
136
+     * @param object $meta
137
+     * @param string $field
138
+     * @return boolean
139
+     */
140
+    public function isValidFieldForLockTime($meta, $field)
141
+    {
142
+        $mapping = $meta->getFieldMapping($field);
143
+        return $mapping && ($mapping['type'] === 'date' || $mapping['type'] === 'datetime' || $mapping['type'] === 'timestamp');
144
+    }
145
+
146
+    /**
147
+     * Checks if $field type is valid for Root field
148
+     *
149
+     * @param object $meta
150
+     * @param string $field
151
+     * @return boolean
152
+     */
153
+    public function isValidFieldForRoot($meta, $field)
154
+    {
155
+        $mapping = $meta->getFieldMapping($field);
156
+        return $mapping && in_array($mapping['type'], $this->validRootTypes);
157
+    }
158
+
159
+    /**
160
+     * Validates metadata for nested type tree
161
+     *
162
+     * @param object $meta
163
+     * @param array $config
164
+     * @throws InvalidMappingException
165
+     * @return void
166
+     */
167
+    public function validateNestedTreeMetadata($meta, array $config)
168
+    {
169
+        $missingFields = array();
170
+        if (!isset($config['parent'])) {
171
+            $missingFields[] = 'ancestor';
172
+        }
173
+        if (!isset($config['left'])) {
174
+            $missingFields[] = 'left';
175
+        }
176
+        if (!isset($config['right'])) {
177
+            $missingFields[] = 'right';
178
+        }
179
+        if ($missingFields) {
180
+            throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
181
+        }
182
+    }
183
+
184
+    /**
185
+     * Validates metadata for closure type tree
186
+     *
187
+     * @param object $meta
188
+     * @param array $config
189
+     * @throws InvalidMappingException
190
+     * @return void
191
+     */
192
+    public function validateClosureTreeMetadata($meta, array $config)
193
+    {
194
+        $missingFields = array();
195
+        if (!isset($config['parent'])) {
196
+            $missingFields[] = 'ancestor';
197
+        }
198
+        if (!isset($config['closure'])) {
199
+            $missingFields[] = 'closure class';
200
+        }
201
+        if ($missingFields) {
202
+            throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
203
+        }
204
+    }
205
+
206
+    /**
207
+     * Validates metadata for materialized path type tree
208
+     *
209
+     * @param object $meta
210
+     * @param array $config
211
+     * @throws InvalidMappingException
212
+     * @return void
213
+     */
214
+    public function validateMaterializedPathTreeMetadata($meta, array $config)
215
+    {
216
+        $missingFields = array();
217
+        if (!isset($config['parent'])) {
218
+            $missingFields[] = 'ancestor';
219
+        }
220
+        if (!isset($config['path'])) {
221
+            $missingFields[] = 'path';
222
+        }
223
+        if (!isset($config['path_source'])) {
224
+            $missingFields[] = 'path_source';
225
+        }
226
+        if ($missingFields) {
227
+            throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
228
+        }
229
+    }
230
+}

+ 514 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/AbstractMaterializedPath.php 查看文件

@@ -0,0 +1,514 @@
1
+<?php
2
+
3
+namespace Gedmo\Tree\Strategy;
4
+
5
+use Gedmo\Tree\Strategy;
6
+use Doctrine\ORM\EntityManager;
7
+use Gedmo\Tree\TreeListener;
8
+use Doctrine\ORM\Mapping\ClassMetadataInfo;
9
+use Doctrine\ORM\Query;
10
+use Doctrine\Common\Persistence\ObjectManager;
11
+use Gedmo\Mapping\Event\AdapterInterface;
12
+use Gedmo\Exception\RuntimeException;
13
+use Gedmo\Exception\TreeLockingException;
14
+
15
+/**
16
+ * This strategy makes tree using materialized path strategy
17
+ *
18
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
19
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
20
+ * @author <rocco@roccosportal.com>
21
+ * @package Gedmo.Tree.Strategy
22
+ * @subpackage AbstractMaterializedPath
23
+ * @link http://www.gediminasm.org
24
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
25
+ */
26
+
27
+abstract class AbstractMaterializedPath implements Strategy
28
+{
29
+    const ACTION_INSERT = 'insert';
30
+    const ACTION_UPDATE = 'update';
31
+    const ACTION_REMOVE = 'remove';
32
+
33
+    /**
34
+     * TreeListener
35
+     *
36
+     * @var AbstractTreeListener
37
+     */
38
+    protected $listener = null;
39
+
40
+    /**
41
+     * Array of objects which were scheduled for path processes
42
+     *
43
+     * @var array
44
+     */
45
+    protected $scheduledForPathProcess = array();
46
+
47
+    /**
48
+     * Array of objects which were scheduled for path process.
49
+     * This time, this array contains the objects with their ID
50
+     * already set
51
+     *
52
+     * @var array
53
+     */
54
+    protected $scheduledForPathProcessWithIdSet = array();
55
+
56
+    /**
57
+     * Roots of trees which needs to be locked
58
+     *
59
+     * @var array
60
+     */
61
+    protected $rootsOfTreesWhichNeedsLocking = array();
62
+
63
+    /**
64
+     * Objects which are going to be inserted (set only if tree locking is used)
65
+     *
66
+     * @var array
67
+     */
68
+    protected $pendingObjectsToInsert = array();
69
+
70
+    /**
71
+     * Objects which are going to be updated (set only if tree locking is used)
72
+     *
73
+     * @var array
74
+     */
75
+    protected $pendingObjectsToUpdate = array();
76
+
77
+    /**
78
+     * Objects which are going to be removed (set only if tree locking is used)
79
+     *
80
+     * @var array
81
+     */
82
+    protected $pendingObjectsToRemove = array();
83
+
84
+    /**
85
+     * {@inheritdoc}
86
+     */
87
+    public function __construct(TreeListener $listener)
88
+    {
89
+        $this->listener = $listener;
90
+    }
91
+
92
+    /**
93
+     * {@inheritdoc}
94
+     */
95
+    public function getName()
96
+    {
97
+        return Strategy::MATERIALIZED_PATH;
98
+    }
99
+
100
+    /**
101
+     * {@inheritdoc}
102
+     */
103
+    public function processScheduledInsertion($om, $node, AdapterInterface $ea)
104
+    {
105
+        $meta = $om->getClassMetadata(get_class($node));
106
+        $config = $this->listener->getConfiguration($om, $meta->name);
107
+        $fieldMapping = $meta->getFieldMapping($config['path_source']);
108
+
109
+        if ($meta->isIdentifier($config['path_source']) || $fieldMapping['type'] === 'string') {
110
+            $this->scheduledForPathProcess[spl_object_hash($node)] = $node;
111
+        } else {
112
+            $this->updateNode($om, $node, $ea);
113
+        }
114
+    }
115
+
116
+    /**
117
+     * {@inheritdoc}
118
+     */
119
+    public function processScheduledUpdate($om, $node, AdapterInterface $ea)
120
+    {
121
+        $meta = $om->getClassMetadata(get_class($node));
122
+        $config = $this->listener->getConfiguration($om, $meta->name);
123
+        $uow = $om->getUnitOfWork();
124
+        $changeSet = $ea->getObjectChangeSet($uow, $node);
125
+
126
+        if (isset($changeSet[$config['parent']]) || isset($changeSet[$config['path_source']])) {
127
+            if (isset($changeSet[$config['path']])) {
128
+                $originalPath = $changeSet[$config['path']][0];
129
+            } else {
130
+                $pathProp = $meta->getReflectionProperty($config['path']);
131
+                $pathProp->setAccessible(true);
132
+                $originalPath = $pathProp->getValue($node);
133
+            }
134
+
135
+            $this->updateNode($om, $node, $ea);
136
+            $this->updateChildren($om, $node, $ea, $originalPath);
137
+        }
138
+    }
139
+
140
+    /**
141
+     * {@inheritdoc}
142
+     */
143
+    public function processPostPersist($om, $node, AdapterInterface $ea)
144
+    {
145
+        $oid = spl_object_hash($node);
146
+
147
+        if ($this->scheduledForPathProcess && array_key_exists($oid, $this->scheduledForPathProcess)) {
148
+            $this->scheduledForPathProcessWithIdSet[$oid] = $node;
149
+
150
+            unset($this->scheduledForPathProcess[$oid]);
151
+
152
+            if (empty($this->scheduledForPathProcess)) {
153
+                foreach ($this->scheduledForPathProcessWithIdSet as $oid => $node) {
154
+                    $this->updateNode($om, $node, $ea);
155
+
156
+                    unset($this->scheduledForPathProcessWithIdSet[$oid]);
157
+                }
158
+            }
159
+        }
160
+
161
+        $this->processPostEventsActions($om, $ea, $node, self::ACTION_INSERT);
162
+    }
163
+
164
+    /**
165
+     * {@inheritdoc}
166
+     */
167
+    public function processPostUpdate($om, $node, AdapterInterface $ea)
168
+    {
169
+        $this->processPostEventsActions($om, $ea, $node, self::ACTION_UPDATE);
170
+    }
171
+
172
+    /**
173
+     * {@inheritdoc}
174
+     */
175
+    public function processPostRemove($om, $node, AdapterInterface $ea)
176
+    {
177
+        $this->processPostEventsActions($om, $ea, $node, self::ACTION_REMOVE);
178
+    }
179
+
180
+    /**
181
+     * {@inheritdoc}
182
+     */
183
+    public function onFlushEnd($om, AdapterInterface $ea)
184
+    {
185
+        $this->lockTrees($om, $ea);
186
+    }
187
+
188
+    /**
189
+     * {@inheritdoc}
190
+     */
191
+    public function processPreRemove($om, $node)
192
+    {
193
+        $this->processPreLockingActions($om, $node, self::ACTION_REMOVE);
194
+    }
195
+
196
+    /**
197
+     * {@inheritdoc}
198
+     */
199
+    public function processPrePersist($om, $node)
200
+    {
201
+        $this->processPreLockingActions($om, $node, self::ACTION_INSERT);
202
+    }
203
+
204
+    /**
205
+     * {@inheritdoc}
206
+     */
207
+    public function processPreUpdate($om, $node)
208
+    {
209
+        $this->processPreLockingActions($om, $node, self::ACTION_UPDATE);
210
+    }
211
+
212
+    /**
213
+     * {@inheritdoc}
214
+     */
215
+    public function processMetadataLoad($om, $meta)
216
+    {}
217
+
218
+    /**
219
+     * {@inheritdoc}
220
+     */
221
+    public function processScheduledDelete($om, $node)
222
+    {
223
+        $meta = $om->getClassMetadata(get_class($node));
224
+        $config = $this->listener->getConfiguration($om, $meta->name);
225
+
226
+        $this->removeNode($om, $meta, $config, $node);
227
+    }
228
+
229
+    /**
230
+     * Update the $node
231
+     *
232
+     * @param ObjectManager $om
233
+     * @param object $node - target node
234
+     * @param object $ea - event adapter
235
+     * @return void
236
+     */
237
+    public function updateNode(ObjectManager $om, $node, AdapterInterface $ea)
238
+    {
239
+        $oid = spl_object_hash($node);
240
+        $meta = $om->getClassMetadata(get_class($node));
241
+        $config = $this->listener->getConfiguration($om, $meta->name);
242
+        $uow = $om->getUnitOfWork();
243
+        $parentProp = $meta->getReflectionProperty($config['parent']);
244
+        $parentProp->setAccessible(true);
245
+        $parent = $parentProp->getValue($node);
246
+        $pathProp = $meta->getReflectionProperty($config['path']);
247
+        $pathProp->setAccessible(true);
248
+        $pathSourceProp = $meta->getReflectionProperty($config['path_source']);
249
+        $pathSourceProp->setAccessible(true);
250
+        $path = $pathSourceProp->getValue($node);
251
+
252
+        // We need to avoid the presence of the path separator in the path source
253
+        if (strpos($path, $config['path_separator']) !== false) {
254
+            $msg = 'You can\'t use the Path separator ("%s") as a character for your PathSource field value.';
255
+
256
+            throw new RuntimeException(sprintf($msg, $config['path_separator']));
257
+        }
258
+
259
+        $fieldMapping = $meta->getFieldMapping($config['path_source']);
260
+
261
+        // default behavior: if PathSource field is a string, we append the ID to the path
262
+        // path_append_id is true: always append id
263
+        // path_append_id is false: never append id
264
+        if ($config['path_append_id'] === true || ($fieldMapping['type'] === 'string' && $config['path_append_id']!==false)) {
265
+            if (method_exists($meta, 'getIdentifierValue')) {
266
+                $identifier = $meta->getIdentifierValue($node);
267
+            } else {
268
+                $identifierProp = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName());
269
+                $identifierProp->setAccessible(true);
270
+                $identifier = $identifierProp->getValue($node);
271
+            }
272
+
273
+            $path .= '-'.$identifier;
274
+        }
275
+
276
+
277
+        if ($parent) {
278
+            // Ensure parent has been initialized in the case where it's a proxy
279
+            $om->initializeObject($parent);
280
+
281
+            $changeSet = $uow->isScheduledForUpdate($parent) ? $ea->getObjectChangeSet($uow, $parent) : false;
282
+            $pathOrPathSourceHasChanged = $changeSet && (isset($changeSet[$config['path_source']]) || isset($changeSet[$config['path']]));
283
+
284
+            if ($pathOrPathSourceHasChanged || !$pathProp->getValue($parent)) {
285
+                $this->updateNode($om, $parent, $ea);
286
+            }
287
+
288
+            $parentPath = $pathProp->getValue($parent);
289
+            // if parent path not ends with separator
290
+            if ($parentPath[strlen($parentPath) - 1] !== $config['path_separator']) {
291
+                // add separator
292
+                $path = $pathProp->getValue($parent) . $config['path_separator'] . $path;
293
+            } else {
294
+                // don't add separator
295
+                $path = $pathProp->getValue($parent) . $path;
296
+            }
297
+
298
+        }
299
+
300
+
301
+        if ($config['path_starts_with_separator'] && (strlen($path) > 0 && $path[0] !== $config['path_separator'])) {
302
+            $path = $config['path_separator'] . $path;
303
+        }
304
+
305
+        if ($config['path_ends_with_separator'] && ($path[strlen($path) - 1] !== $config['path_separator'])) {
306
+            $path .= $config['path_separator'];
307
+        }
308
+
309
+        $pathProp->setValue($node, $path);
310
+        $changes = array(
311
+            $config['path'] => array(null, $path)
312
+        );
313
+
314
+        if (isset($config['path_hash'])) {
315
+            $pathHash = md5($path);
316
+            $pathHashProp = $meta->getReflectionProperty($config['path_hash']);
317
+            $pathHashProp->setAccessible(true);
318
+            $pathHashProp->setValue($node, $pathHash);
319
+            $changes[$config['path_hash']] = array(null, $pathHash);
320
+        }
321
+
322
+
323
+        if (isset($config['level'])) {
324
+            $level = substr_count($path, $config['path_separator']);
325
+            $levelProp = $meta->getReflectionProperty($config['level']);
326
+            $levelProp->setAccessible(true);
327
+            $levelProp->setValue($node, $level);
328
+            $changes[$config['level']] = array(null, $level);
329
+        }
330
+
331
+        $uow->scheduleExtraUpdate($node, $changes);
332
+        $ea->setOriginalObjectProperty($uow, $oid, $config['path'], $path);
333
+
334
+        if(isset($config['path_hash'])){
335
+            $ea->setOriginalObjectProperty($uow, $oid, $config['path_hash'], $pathHash);
336
+        }
337
+    }
338
+
339
+    /**
340
+     * Update node's children
341
+     *
342
+     * @param ObjectManager $om
343
+     * @param object $node
344
+     * @param AdapterInterface $ea
345
+     * @param string $originalPath
346
+     * @return void
347
+     */
348
+    public function updateChildren(ObjectManager $om, $node, AdapterInterface $ea, $originalPath)
349
+    {
350
+        $meta = $om->getClassMetadata(get_class($node));
351
+        $config = $this->listener->getConfiguration($om, $meta->name);
352
+        $children = $this->getChildren($om, $meta, $config, $originalPath);
353
+
354
+        foreach ($children as $child) {
355
+            $this->updateNode($om, $child, $ea);
356
+        }
357
+    }
358
+
359
+    /**
360
+     * Process pre-locking actions
361
+     *
362
+     * @param ObjectManager $om
363
+     * @param object $node
364
+     * @param string $action
365
+     * @return void
366
+     */
367
+    public function processPreLockingActions($om, $node, $action)
368
+    {
369
+        $meta = $om->getClassMetadata(get_class($node));
370
+        $config = $this->listener->getConfiguration($om, $meta->name);
371
+
372
+        if ($config['activate_locking']) {;
373
+            $parentProp = $meta->getReflectionProperty($config['parent']);
374
+            $parentProp->setAccessible(true);
375
+            $parentNode = $node;
376
+
377
+            while (!is_null($parent = $parentProp->getValue($parentNode))) {
378
+                $parentNode = $parent;
379
+            }
380
+
381
+            // In some cases, the parent could be a not initialized proxy. In this case, the
382
+            // "lockTime" field may NOT be loaded yet and have null instead of the date.
383
+            // We need to be sure that this field has its real value
384
+            if ($parentNode !== $node && $parentNode instanceof \Doctrine\ODM\MongoDB\Proxy\Proxy) {
385
+                $reflMethod = new \ReflectionMethod(get_class($parentNode), '__load');
386
+                $reflMethod->setAccessible(true);
387
+
388
+                $reflMethod->invoke($parentNode);
389
+            }
390
+
391
+            // If tree is already locked, we throw an exception
392
+            $lockTimeProp = $meta->getReflectionProperty($config['lock_time']);
393
+            $lockTimeProp->setAccessible(true);
394
+            $lockTime = $lockTimeProp->getValue($parentNode);
395
+
396
+            if (!is_null($lockTime)) {
397
+                $lockTime = $lockTime instanceof \MongoDate ? $lockTime->sec : $lockTime->getTimestamp();
398
+            }
399
+
400
+            if (!is_null($lockTime) && ($lockTime >= (time() - $config['locking_timeout']))) {
401
+                $msg = 'Tree with root id "%s" is locked.';
402
+                $id = $meta->getIdentifierValue($parentNode);
403
+
404
+                throw new TreeLockingException(sprintf($msg, $id));
405
+            }
406
+
407
+            $this->rootsOfTreesWhichNeedsLocking[spl_object_hash($parentNode)] = $parentNode;
408
+
409
+            $oid = spl_object_hash($node);
410
+
411
+            switch ($action) {
412
+                case self::ACTION_INSERT:
413
+                    $this->pendingObjectsToInsert[$oid] = $node;
414
+
415
+                    break;
416
+                case self::ACTION_UPDATE:
417
+                    $this->pendingObjectsToUpdate[$oid] = $node;
418
+
419
+                    break;
420
+                case self::ACTION_REMOVE:
421
+                    $this->pendingObjectsToRemove[$oid] = $node;
422
+
423
+                    break;
424
+                default:
425
+                    throw new \InvalidArgumentException(sprintf('"%s" is not a valid action.', $action));
426
+            }
427
+        }
428
+    }
429
+
430
+    /**
431
+     * Process pre-locking actions
432
+     *
433
+     * @param ObjectManager $om
434
+     * @param AdapterInterface $ea
435
+     * @param object $node
436
+     * @param string $action
437
+     * @return void
438
+     */
439
+    public function processPostEventsActions(ObjectManager $om, AdapterInterface $ea, $node, $action)
440
+    {
441
+        $meta = $om->getClassMetadata(get_class($node));
442
+        $config = $this->listener->getConfiguration($om, $meta->name);
443
+
444
+        if ($config['activate_locking']) {
445
+            switch ($action) {
446
+                case self::ACTION_INSERT:
447
+                    unset($this->pendingObjectsToInsert[spl_object_hash($node)]);
448
+
449
+                    break;
450
+                case self::ACTION_UPDATE:
451
+                    unset($this->pendingObjectsToUpdate[spl_object_hash($node)]);
452
+
453
+                    break;
454
+                case self::ACTION_REMOVE:
455
+                    unset($this->pendingObjectsToRemove[spl_object_hash($node)]);
456
+
457
+                    break;
458
+                default:
459
+                    throw new \InvalidArgumentException(sprintf('"%s" is not a valid action.', $action));
460
+            }
461
+
462
+            if (empty($this->pendingObjectsToInsert) && empty($this->pendingObjectsToUpdate) &&
463
+                empty($this->pendingObjectsToRemove)) {
464
+                $this->releaseTreeLocks($om, $ea);
465
+            }
466
+        }
467
+    }
468
+
469
+    /**
470
+     * Locks all needed trees
471
+     *
472
+     * @param ObjectManager $om
473
+     * @param AdapterInterface $ea
474
+     * @return void
475
+     */
476
+    protected function lockTrees(ObjectManager $om, AdapterInterface $ea)
477
+    {
478
+        // Do nothing by default
479
+    }
480
+
481
+    /**
482
+     * Releases all trees which are locked
483
+     *
484
+     * @param ObjectManager $om
485
+     * @param AdapterInterface $ea
486
+     * @return void
487
+     */
488
+    protected function releaseTreeLocks(ObjectManager $om, AdapterInterface $ea)
489
+    {
490
+        // Do nothing by default
491
+    }
492
+
493
+    /**
494
+     * Remove node and its children
495
+     *
496
+     * @param ObjectManager $om
497
+     * @param object $meta - Metadata
498
+     * @param object $config - config
499
+     * @param object $node - node to remove
500
+     * @return void
501
+     */
502
+    abstract public function removeNode($om, $meta, $config, $node);
503
+
504
+    /**
505
+     * Returns children of the node with its original path
506
+     *
507
+     * @param ObjectManager $om
508
+     * @param object $meta - Metadata
509
+     * @param object $config - config
510
+     * @param string $originalPath - original path of object
511
+     * @return Doctrine\ODM\MongoDB\Cursor
512
+     */
513
+    abstract public function getChildren($om, $meta, $config, $originalPath);
514
+}

+ 28 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php 查看文件

@@ -0,0 +1,28 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable\FilenameGenerator;
4
+
5
+/**
6
+ * FilenameGeneratorAlphanumeric
7
+ *
8
+ * This class generates a filename, leaving only lowercase
9
+ * alphanumeric characters
10
+ *
11
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @package Gedmo.Uploadable.FilenameGenerator
14
+ * @subpackage FilenameGeneratorAlphanumeric
15
+ * @link http://www.gediminasm.org
16
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
17
+ */
18
+
19
+class FilenameGeneratorAlphanumeric implements FilenameGeneratorInterface
20
+{
21
+    /**
22
+     * @inheritDoc
23
+     */
24
+    public static function generate($filename, $extension, $object = null)
25
+    {
26
+        return preg_replace('/[^a-z0-9]+/', '-', strtolower($filename)).$extension;
27
+    }
28
+}

+ 28 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php 查看文件

@@ -0,0 +1,28 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable\FilenameGenerator;
4
+
5
+/**
6
+ * FilenameGeneratorInterface
7
+ *
8
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
9
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
10
+ * @package Gedmo.Uploadable.FilenameGenerator
11
+ * @subpackage FilenameGeneratorInterface
12
+ * @link http://www.gediminasm.org
13
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14
+ */
15
+
16
+interface FilenameGeneratorInterface
17
+{
18
+    /**
19
+     * Generates a new filename
20
+     *
21
+     * @param string - Filename without extension
22
+     * @param string - Extension with dot: .jpg, .gif, etc
23
+     * @param $object
24
+     *
25
+     * @return string
26
+     */
27
+    public static function generate($filename, $extension, $object = null);
28
+}

+ 25 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php 查看文件

@@ -0,0 +1,25 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable\FilenameGenerator;
4
+
5
+/**
6
+ * FilenameGeneratorSha1
7
+ *
8
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
9
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
10
+ * @package Gedmo.Uploadable.FilenameGenerator
11
+ * @subpackage FilenameGeneratorSha1
12
+ * @link http://www.gediminasm.org
13
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14
+ */
15
+
16
+class FilenameGeneratorSha1 implements FilenameGeneratorInterface
17
+{
18
+    /**
19
+     * @inheritDoc
20
+     */
21
+    public static function generate($filename, $extension, $object = null)
22
+    {
23
+        return sha1(uniqid($filename.$extension, true)).$extension;
24
+    }
25
+}

+ 709 - 0
vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php 查看文件

@@ -0,0 +1,709 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable;
4
+
5
+use Doctrine\Common\Persistence\ObjectManager,
6
+    Doctrine\Common\Persistence\Mapping\ClassMetadata,
7
+    Doctrine\ORM\UnitOfWork,
8
+    Gedmo\Mapping\MappedEventSubscriber,
9
+    Doctrine\Common\EventArgs,
10
+    Gedmo\Mapping\Event\AdapterInterface,
11
+    Gedmo\Exception\UploadableDirectoryNotFoundException,
12
+    Gedmo\Exception\UploadablePartialException,
13
+    Gedmo\Exception\UploadableCantWriteException,
14
+    Gedmo\Exception\UploadableExtensionException,
15
+    Gedmo\Exception\UploadableFormSizeException,
16
+    Gedmo\Exception\UploadableIniSizeException,
17
+    Gedmo\Exception\UploadableNoFileException,
18
+    Gedmo\Exception\UploadableNoTmpDirException,
19
+    Gedmo\Exception\UploadableUploadException,
20
+    Gedmo\Exception\UploadableFileAlreadyExistsException,
21
+    Gedmo\Exception\UploadableNoPathDefinedException,
22
+    Gedmo\Exception\UploadableMaxSizeException,
23
+    Gedmo\Exception\UploadableInvalidMimeTypeException,
24
+    Gedmo\Exception\UploadableCouldntGuessMimeTypeException,
25
+    Gedmo\Uploadable\Mapping\Validator,
26
+    Gedmo\Uploadable\FileInfo\FileInfoInterface,
27
+    Gedmo\Uploadable\FileInfo\FileInfoArray,
28
+    Gedmo\Uploadable\MimeType\MimeTypeGuesser,
29
+    Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface,
30
+    Gedmo\Uploadable\MimeType\MimeTypesExtensionsMap,
31
+    Doctrine\Common\NotifyPropertyChanged,
32
+    Gedmo\Uploadable\Events,
33
+    Gedmo\Uploadable\Event\UploadablePreFileProcessEventArgs,
34
+    Gedmo\Uploadable\Event\UploadablePostFileProcessEventArgs;
35
+
36
+/**
37
+ * Uploadable listener
38
+ *
39
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
40
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
41
+ * @package Gedmo.Uploadable
42
+ * @subpackage UploadableListener
43
+ * @link http://www.gediminasm.org
44
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
45
+ */
46
+class UploadableListener extends MappedEventSubscriber
47
+{
48
+    const ACTION_INSERT = 'INSERT';
49
+    const ACTION_UPDATE = 'UPDATE';
50
+
51
+
52
+    /**
53
+     * Default path to move files in
54
+     *
55
+     * @var string
56
+     */
57
+    private $defaultPath;
58
+
59
+    /**
60
+     * Mime type guesser
61
+     *
62
+     * @var Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface
63
+     */
64
+    private $mimeTypeGuesser;
65
+
66
+    /**
67
+     * Default FileInfoInterface class
68
+     *
69
+     * @var string
70
+     */
71
+    private $defaultFileInfoClass = 'Gedmo\Uploadable\FileInfo\FileInfoArray';
72
+
73
+    /**
74
+     * Array of files to remove on postFlush
75
+     *
76
+     * @var array
77
+     */
78
+    private $pendingFileRemovals = array();
79
+
80
+    /**
81
+     * Array of FileInfoInterface objects. The index is the hash of the entity owner
82
+     * of the FileInfoInterface object.
83
+     *
84
+     * @var array
85
+     */
86
+    private $fileInfoObjects = array();
87
+
88
+
89
+
90
+    public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null)
91
+    {
92
+        $this->mimeTypeGuesser = $mimeTypeGuesser ? $mimeTypeGuesser : new MimeTypeGuesser();
93
+    }
94
+
95
+    /**
96
+     * {@inheritdoc}
97
+     */
98
+    public function getSubscribedEvents()
99
+    {
100
+        return array(
101
+            'loadClassMetadata',
102
+            'preFlush',
103
+            'onFlush',
104
+            'postFlush'
105
+        );
106
+    }
107
+
108
+    /**
109
+     * This event is needed in special cases where the entity needs to be updated, but it only has the
110
+     * file field modified. Since we can't mark an entity as "dirty" in the "addEntityFileInfo" method,
111
+     * doctrine thinks the entity has no changes, which produces that the "onFlush" event gets never called.
112
+     * Here we mark the entity as dirty, so the "onFlush" event gets called, and the file is processed.
113
+     *
114
+     * @param \Doctrine\Common\EventArgs $args
115
+     */
116
+    public function preFlush(EventArgs $args)
117
+    {
118
+        if (empty($this->fileInfoObjects)) {
119
+            // Nothing to do
120
+            return;
121
+        }
122
+
123
+        $ea = $this->getEventAdapter($args);
124
+        $om = $ea->getObjectManager();
125
+        $uow = $om->getUnitOfWork();
126
+        $first = reset($this->fileInfoObjects);
127
+        $meta = $om->getClassMetadata(get_class($first['entity']));
128
+        $config = $this->getConfiguration($om, $meta->name);
129
+
130
+        foreach ($this->fileInfoObjects as $info) {
131
+            $entity = $info['entity'];
132
+
133
+            // If the entity is in the identity map, it means it will be updated. We need to force the
134
+            // "dirty check" here by "modifying" the path. We are actually setting the same value, but
135
+            // this will mark the entity as dirty, and the "onFlush" event will be fired, even if there's
136
+            // no other change in the entity's fields apart from the file itself.
137
+            if ($uow->isInIdentityMap($entity)) {
138
+                $path = $this->getFilePath($meta, $config, $entity);
139
+
140
+                $uow->propertyChanged($entity, $config['filePathField'], $path, $path);
141
+                $uow->scheduleForUpdate($entity);
142
+            }
143
+        }
144
+    }
145
+
146
+    /**
147
+     * Handle file-uploading depending on the action
148
+     * being done with objects
149
+     *
150
+     * @param \Doctrine\Common\EventArgs $args
151
+     */
152
+    public function onFlush(EventArgs $args)
153
+    {
154
+        $ea = $this->getEventAdapter($args);
155
+        $om = $ea->getObjectManager();
156
+        $uow = $om->getUnitOfWork();
157
+
158
+        // Do we need to upload files?
159
+        foreach ($this->fileInfoObjects as $info) {
160
+            $entity = $info['entity'];
161
+            $scheduledForInsert = $uow->isScheduledForInsert($entity);
162
+            $scheduledForUpdate = $uow->isScheduledForUpdate($entity);
163
+            $action = ($scheduledForInsert || $scheduledForUpdate) ?
164
+                ($scheduledForInsert ? self::ACTION_INSERT : self::ACTION_UPDATE) :
165
+                false;
166
+
167
+            if ($action) {
168
+                $this->processFile($ea, $entity, $action);
169
+            }
170
+        }
171
+
172
+        // Do we need to remove any files?
173
+        foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
174
+            $meta = $om->getClassMetadata(get_class($object));
175
+            
176
+            if ($config = $this->getConfiguration($om, $meta->name)) {
177
+                if (isset($config['uploadable']) && $config['uploadable']) {
178
+                    $this->pendingFileRemovals[] = $this->getFilePath($meta, $config, $object);
179
+                }
180
+            }
181
+        }
182
+    }
183
+
184
+    /**
185
+     * Handle removal of files
186
+     *
187
+     * @param \Doctrine\Common\EventArgs $args
188
+     */
189
+    public function postFlush(EventArgs $args)
190
+    {
191
+        if (!empty($this->pendingFileRemovals)) {
192
+            foreach ($this->pendingFileRemovals as $file) {
193
+                $this->removeFile($file);
194
+            }
195
+
196
+            $this->pendingFileRemovals = array();
197
+        }
198
+
199
+        $this->fileInfoObjects = array();
200
+    }
201
+
202
+    /**
203
+     * If it's a Uploadable object, verify if the file was uploaded.
204
+     * If that's the case, process it.
205
+     *
206
+     * @param \Gedmo\Mapping\Event\AdapterInterface $ea
207
+     * @param $object
208
+     * @param $action
209
+     * @throws \Gedmo\Exception\UploadableNoPathDefinedException
210
+     * @throws \Gedmo\Exception\UploadableCouldntGuessMimeTypeException
211
+     * @throws \Gedmo\Exception\UploadableMaxSizeException
212
+     * @throws \Gedmo\Exception\UploadableInvalidMimeTypeException
213
+     */
214
+    public function processFile(AdapterInterface $ea, $object, $action)
215
+    {
216
+        $oid = spl_object_hash($object);
217
+        $om = $ea->getObjectManager();
218
+        $uow = $om->getUnitOfWork();
219
+        $meta = $om->getClassMetadata(get_class($object));
220
+        $config = $this->getConfiguration($om, $meta->name);
221
+
222
+        if (!$config || !isset($config['uploadable']) || !$config['uploadable']) {
223
+            // Nothing to do
224
+            return;
225
+        }
226
+
227
+        $refl = $meta->getReflectionClass();
228
+        $fileInfo = $this->fileInfoObjects[$oid]['fileInfo'];
229
+        $evm = $om->getEventManager();
230
+
231
+        if ($evm->hasListeners(Events::uploadablePreFileProcess)) {
232
+            $evm->dispatchEvent(Events::uploadablePreFileProcess, new UploadablePreFileProcessEventArgs(
233
+                $this,
234
+                $om,
235
+                $config,
236
+                $fileInfo,
237
+                $object,
238
+                $action
239
+            ));
240
+        }
241
+
242
+        // Validations
243
+        if ($config['maxSize'] > 0 && $fileInfo->getSize() > $config['maxSize']) {
244
+            $msg = 'File "%s" exceeds the maximum allowed size of %d bytes. File size: %d bytes';
245
+
246
+            throw new UploadableMaxSizeException(sprintf($msg,
247
+                $fileInfo->getName(),
248
+                $config['maxSize'],
249
+                $fileInfo->getSize()
250
+            ));
251
+        }
252
+
253
+        $mime = $this->mimeTypeGuesser->guess($fileInfo->getTmpName());
254
+
255
+        if (!$mime) {
256
+            throw new UploadableCouldntGuessMimeTypeException(sprintf('Couldn\'t guess mime type for file "%s".',
257
+                $fileInfo->getName()
258
+            ));
259
+        }
260
+
261
+        if ($config['allowedTypes'] || $config['disallowedTypes']) {
262
+            $ok = $config['allowedTypes'] ? false : true;
263
+            $mimes = $config['allowedTypes'] ? $config['allowedTypes'] : $config['disallowedTypes'];
264
+
265
+            foreach ($mimes as $m) {
266
+                if ($mime === $m) {
267
+                    $ok = $config['allowedTypes'] ? true : false;
268
+
269
+                    break;
270
+                }
271
+            }
272
+
273
+            if (!$ok) {
274
+                throw new UploadableInvalidMimeTypeException(sprintf('Invalid mime type "%s" for file "%s".',
275
+                    $mime,
276
+                    $fileInfo->getName()
277
+                ));
278
+            }
279
+        }
280
+
281
+        $filePathField = $refl->getProperty($config['filePathField']);
282
+        $filePathField->setAccessible(true);
283
+
284
+        $path = $config['path'];
285
+
286
+        if ($path === '') {
287
+            if ($config['pathMethod'] !== '') {
288
+                $pathMethod = $refl->getMethod($config['pathMethod']);
289
+                $pathMethod->setAccessible(true);
290
+                $path = $pathMethod->invoke($object);
291
+            } else if ($this->getDefaultPath() !== null) {
292
+                $path = $this->getDefaultPath();
293
+            } else {
294
+                $msg = 'You have to define the path to save files either in the listener, or in the class "%s"';
295
+
296
+                throw new UploadableNoPathDefinedException(sprintf($msg,
297
+                    $meta->name
298
+                ));
299
+            }
300
+        }
301
+
302
+        Validator::validatePath($path);
303
+
304
+        $path = rtrim($path, '\/');
305
+
306
+        if ($config['fileMimeTypeField']) {
307
+            $fileMimeTypeField = $refl->getProperty($config['fileMimeTypeField']);
308
+            $fileMimeTypeField->setAccessible(true);
309
+        }
310
+
311
+        if ($config['fileSizeField']) {
312
+            $fileSizeField = $refl->getProperty($config['fileSizeField']);
313
+            $fileSizeField->setAccessible(true);
314
+        }
315
+
316
+        if ($action === self::ACTION_UPDATE) {
317
+            // First we add the original file to the pendingFileRemovals array
318
+            $this->pendingFileRemovals[] = $this->getFilePath($meta, $config, $object);
319
+        }
320
+
321
+        // We generate the filename based on configuration
322
+        $generatorNamespace = 'Gedmo\Uploadable\FilenameGenerator';
323
+
324
+        switch ($config['filenameGenerator']) {
325
+            case Validator::FILENAME_GENERATOR_ALPHANUMERIC:
326
+                $generatorClass = $generatorNamespace .'\FilenameGeneratorAlphanumeric';
327
+
328
+                break;
329
+            case Validator::FILENAME_GENERATOR_SHA1:
330
+                $generatorClass = $generatorNamespace .'\FilenameGeneratorSha1';
331
+
332
+                break;
333
+            case Validator::FILENAME_GENERATOR_NONE:
334
+                $generatorClass = false;
335
+
336
+                break;
337
+            default:
338
+                $generatorClass = $config['filenameGenerator'];
339
+        }
340
+
341
+        $info = $this->moveFile($fileInfo, $path, $generatorClass, $config['allowOverwrite'], $config['appendNumber'], $object);
342
+
343
+        // We override the mime type with the guessed one
344
+        $info['fileMimeType'] = $mime;
345
+
346
+        $filePathField->setValue($object, $info['filePath']);
347
+
348
+        if ($config['callback'] !== '') {
349
+            $callbackMethod = $refl->getMethod($config['callback']);
350
+            $callbackMethod->setAccessible(true);
351
+
352
+            $callbackMethod->invokeArgs($object, array($info));
353
+        }
354
+
355
+        $changes = array(
356
+            $config['filePathField'] => array($filePathField->getValue($object), $info['filePath'])
357
+        );
358
+
359
+        if ($config['fileMimeTypeField']) {
360
+            $changes[$config['fileMimeTypeField']] = array($fileMimeTypeField->getValue($object), $info['fileMimeType']);
361
+
362
+            $this->updateField($object, $uow, $ea, $meta, $config['fileMimeTypeField'], $info['fileMimeType']);
363
+        }
364
+
365
+        if ($config['fileSizeField']) {
366
+            $changes[$config['fileSizeField']] = array($fileSizeField->getValue($object), $info['fileSize']);
367
+
368
+            $this->updateField($object, $uow, $ea, $meta, $config['fileSizeField'], $info['fileSize']);
369
+        }
370
+
371
+        $this->updateField($object, $uow, $ea, $meta, $config['filePathField'], $info['filePath']);
372
+
373
+        $ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
374
+
375
+        if ($evm->hasListeners(Events::uploadablePostFileProcess)) {
376
+            $evm->dispatchEvent(Events::uploadablePostFileProcess, new UploadablePostFileProcessEventArgs(
377
+                $this,
378
+                $om,
379
+                $config,
380
+                $fileInfo,
381
+                $object,
382
+                $action
383
+            ));
384
+        }
385
+
386
+        unset($this->fileInfoObjects[$oid]);
387
+    }
388
+
389
+    /**
390
+     * Returns the path of the entity's file
391
+     *
392
+     * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $meta
393
+     * @param array $config
394
+     * @param $object
395
+     * @return mixed
396
+     */
397
+    public function getFilePath(ClassMetadata $meta, array $config, $object)
398
+    {
399
+        $refl = $meta->getReflectionClass();
400
+        $filePathField = $refl->getProperty($config['filePathField']);
401
+        $filePathField->setAccessible(true);
402
+        $filePath = $filePathField->getValue($object);
403
+
404
+        return $filePath;
405
+    }
406
+
407
+    /**
408
+     * Simple wrapper for the function "unlink" to ease testing
409
+     *
410
+     * @param string
411
+     *
412
+     * @return bool
413
+     */
414
+    public function removeFile($filePath)
415
+    {
416
+        if (is_file($filePath)) {
417
+            return unlink($filePath);
418
+        }
419
+
420
+        return false;
421
+    }
422
+
423
+    /**
424
+     * Moves the file to the specified path
425
+     *
426
+     * @param FileInfo\FileInfoInterface $fileInfo
427
+     * @param $path
428
+     * @param bool $filenameGeneratorClass
429
+     * @param bool $overwrite
430
+     * @param bool $appendNumber
431
+     * @param $object
432
+     * @return array
433
+     * @throws \Gedmo\Exception\UploadableUploadException
434
+     * @throws \Gedmo\Exception\UploadableNoFileException
435
+     * @throws \Gedmo\Exception\UploadableExtensionException
436
+     * @throws \Gedmo\Exception\UploadableIniSizeException
437
+     * @throws \Gedmo\Exception\UploadableFormSizeException
438
+     * @throws \Gedmo\Exception\UploadableFileAlreadyExistsException
439
+     * @throws \Gedmo\Exception\UploadablePartialException
440
+     * @throws \Gedmo\Exception\UploadableNoTmpDirException
441
+     * @throws \Gedmo\Exception\UploadableCantWriteException
442
+     */
443
+    public function moveFile(FileInfoInterface $fileInfo, $path, $filenameGeneratorClass = false, $overwrite = false, $appendNumber = false, $object)
444
+    {
445
+        if ($fileInfo->getError() > 0) {
446
+            switch ($fileInfo->getError()) {
447
+                case 1:
448
+                    $msg = 'Size of uploaded file "%s" exceeds limit imposed by directive "upload_max_filesize" in php.ini';
449
+
450
+                    throw new UploadableIniSizeException(sprintf($msg, $fileInfo->getName()));
451
+                case 2:
452
+                    $msg = 'Size of uploaded file "%s" exceeds limit imposed by option MAX_FILE_SIZE in your form.';
453
+
454
+                    throw new UploadableFormSizeException(sprintf($msg, $fileInfo->getName()));
455
+                case 3:
456
+                    $msg = 'File "%s" was partially uploaded.';
457
+
458
+                    throw new UploadablePartialException(sprintf($msg, $fileInfo->getName()));
459
+                case 4:
460
+                    $msg = 'No file was uploaded!';
461
+
462
+                    throw new UploadableNoFileException(sprintf($msg, $fileInfo->getName()));
463
+                case 6:
464
+                    $msg = 'Upload failed. Temp dir is missing.';
465
+
466
+                    throw new UploadableNoTmpDirException($msg);
467
+                case 7:
468
+                    $msg = 'File "%s" couldn\'t be uploaded because directory is not writable.';
469
+
470
+                    throw new UploadableCantWriteException(sprintf($msg, $fileInfo->getName()));
471
+                case 8:
472
+                    $msg = 'A PHP Extension stopped the uploaded for some reason.';
473
+
474
+                    throw new UploadableExtensionException(sprintf($msg, $fileInfo->getName()));
475
+                default:
476
+                    throw new UploadableUploadException(sprintf('There was an unknown problem while uploading file "%s"',
477
+                        $fileInfo->getName()
478
+                    ));
479
+            }
480
+        }
481
+
482
+        $info = array(
483
+            'fileName'          => '',
484
+            'fileExtension'     => '',
485
+            'fileWithoutExt'    => '',
486
+            'filePath'          => '',
487
+            'fileMimeType'      => $fileInfo->getType(),
488
+            'fileSize'          => $fileInfo->getSize()
489
+        );
490
+
491
+        $info['fileName'] = basename($fileInfo->getName());
492
+        $info['filePath'] = $path.'/'.$info['fileName'];
493
+
494
+        $hasExtension = strrpos($info['fileName'], '.');
495
+
496
+        if ($hasExtension) {
497
+            $info['fileExtension'] = substr($info['filePath'], strrpos($info['filePath'], '.'));
498
+            $info['fileWithoutExt'] = substr($info['filePath'], 0, strrpos($info['filePath'], '.'));
499
+        } else {
500
+            $info['fileWithoutExt'] = $info['fileName'];
501
+        }
502
+
503
+        // Now we generate the filename using the configured class
504
+        if ($filenameGeneratorClass) {
505
+            $filename = $filenameGeneratorClass::generate(
506
+                str_replace($path.'/', '', $info['fileWithoutExt']),
507
+                $info['fileExtension'],
508
+                $object
509
+            );
510
+            $info['filePath'] = str_replace(
511
+                '/'.$info['fileName'],
512
+                '/'.$filename,
513
+                $info['filePath']
514
+            );
515
+            $info['fileName'] = $filename;
516
+
517
+            if (($pos = strrpos($info['filePath'], '.')) !== false) {
518
+                $info['fileWithoutExt'] = substr($info['filePath'], 0, $pos);
519
+            } else {
520
+                $info['fileWithoutExt'] = $info['filePath'];
521
+            }
522
+        }
523
+
524
+        if (is_file($info['filePath'])) {
525
+            if ($overwrite) {
526
+                $this->removeFile($info['filePath']);
527
+            } else if ($appendNumber) {
528
+                $counter = 1;
529
+                $info['filePath'] = $info['fileWithoutExt'].'-'.$counter.$info['fileExtension'];
530
+
531
+                do {
532
+                    $info['filePath'] = $info['fileWithoutExt'].'-'.(++$counter).$info['fileExtension'];
533
+                } while (is_file($info['filePath']));
534
+            } else {
535
+                throw new UploadableFileAlreadyExistsException(sprintf('File "%s" already exists!',
536
+                    $info['filePath']
537
+                ));
538
+            }
539
+        }
540
+
541
+        if (!$this->doMoveFile($fileInfo->getTmpName(), $info['filePath'], $fileInfo->isUploadedFile())) {
542
+            throw new UploadableUploadException(sprintf('File "%s" was not uploaded, or there was a problem moving it to the location "%s".',
543
+                $fileInfo->getName(),
544
+                $path
545
+            ));
546
+        }
547
+
548
+        return $info;
549
+    }
550
+
551
+    /**
552
+     * Simple wrapper method used to move the file. If it's an uploaded file
553
+     * it will use the "move_uploaded_file method. If it's not, it will
554
+     * simple move it
555
+     *
556
+     * @param string - Source file
557
+     * @param string - Destination file
558
+     * @param bool - Is an uploaded file?
559
+     *
560
+     * @return bool
561
+     */
562
+    public function doMoveFile($source, $dest, $isUploadedFile = true)
563
+    {
564
+        return $isUploadedFile ? @move_uploaded_file($source, $dest) : @copy($source, $dest);
565
+    }
566
+
567
+    /**
568
+     * Maps additional metadata
569
+     *
570
+     * @param \Doctrine\Common\EventArgs $eventArgs
571
+     */
572
+    public function loadClassMetadata(EventArgs $eventArgs)
573
+    {
574
+        $ea = $this->getEventAdapter($eventArgs);
575
+        $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
576
+    }
577
+
578
+    /**
579
+     * Sets the default path
580
+     *
581
+     * @param string
582
+     *
583
+     * @return void
584
+     */
585
+    public function setDefaultPath($path)
586
+    {
587
+        $this->defaultPath = $path;
588
+    }
589
+
590
+    /**
591
+     * Returns default path
592
+     *
593
+     * @return string
594
+     */
595
+    public function getDefaultPath()
596
+    {
597
+        return $this->defaultPath;
598
+    }
599
+
600
+    /**
601
+     * Sets file info default class
602
+     *
603
+     * @param string
604
+     *
605
+     * @return void
606
+     */
607
+    public function setDefaultFileInfoClass($defaultFileInfoClass)
608
+    {
609
+        $fileInfoInterface = 'Gedmo\\Uploadable\\FileInfo\\FileInfoInterface';
610
+        $refl = is_string($defaultFileInfoClass) && class_exists($defaultFileInfoClass) ?
611
+            new \ReflectionClass($defaultFileInfoClass) :
612
+            false;
613
+
614
+        if (!$refl || !$refl->implementsInterface($fileInfoInterface)) {
615
+            $msg = sprintf('Default FileInfo class must be a valid class, and it must implement "%s".',
616
+                $fileInfoInterface
617
+            );
618
+
619
+            throw new \Gedmo\Exception\InvalidArgumentException($msg);
620
+        }
621
+
622
+        $this->defaultFileInfoClass = $defaultFileInfoClass;
623
+    }
624
+
625
+    /**
626
+     * Returns file info default class
627
+     *
628
+     * @return string
629
+     */
630
+    public function getDefaultFileInfoClass()
631
+    {
632
+        return $this->defaultFileInfoClass;
633
+    }
634
+
635
+    /**
636
+     * Adds a FileInfoInterface object for the given entity
637
+     *
638
+     * @param $entity
639
+     * @param $fileInfo
640
+     * @throws \RuntimeException
641
+     */
642
+    public function addEntityFileInfo($entity, $fileInfo)
643
+    {
644
+        $fileInfoClass = $this->getDefaultFileInfoClass();
645
+        $fileInfo = is_array($fileInfo) ? new $fileInfoClass($fileInfo) : $fileInfo;
646
+
647
+        if (!is_object($fileInfo) || !($fileInfo instanceof FileInfoInterface)) {
648
+            $msg = 'You must pass an instance of FileInfoInterface or a valid array for entity of class "%s".';
649
+
650
+            throw new \RuntimeException(sprintf($msg,
651
+                get_class($entity)
652
+            ));
653
+        }
654
+
655
+        $this->fileInfoObjects[spl_object_hash($entity)] = array(
656
+            'entity'        => $entity,
657
+            'fileInfo'      => $fileInfo
658
+        );
659
+    }
660
+
661
+    public function getEntityFileInfo($entity)
662
+    {
663
+        $oid = spl_object_hash($entity);
664
+
665
+        if (!isset($this->fileInfoObjects[$oid])) {
666
+            throw new \RuntimeException(sprintf('There\'s no FileInfoInterface object for entity of class "%s".',
667
+                get_class($entity)
668
+            ));
669
+        }
670
+
671
+        return $this->fileInfoObjects[$oid]['fileInfo'];
672
+    }
673
+
674
+    /**
675
+     * {@inheritDoc}
676
+     */
677
+    protected function getNamespace()
678
+    {
679
+        return __NAMESPACE__;
680
+    }
681
+
682
+    /**
683
+     * @param \Gedmo\Uploadable\Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface $mimeTypeGuesser
684
+     */
685
+    public function setMimeTypeGuesser(MimeTypeGuesserInterface $mimeTypeGuesser)
686
+    {
687
+        $this->mimeTypeGuesser = $mimeTypeGuesser;
688
+    }
689
+
690
+    /**
691
+     * @return \Gedmo\Uploadable\Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface
692
+     */
693
+    public function getMimeTypeGuesser()
694
+    {
695
+        return $this->mimeTypeGuesser;
696
+    }
697
+
698
+    protected function updateField($object, $uow, AdapterInterface $ea, $meta, $field, $value, $notifyPropertyChanged = true)
699
+    {
700
+        $property = $meta->getReflectionProperty($field);
701
+        $oldValue = $property->getValue($object);
702
+        $property->setValue($object, $value);
703
+
704
+        if ($notifyPropertyChanged && $object instanceof NotifyPropertyChanged) {
705
+            $uow = $ea->getObjectManager()->getUnitOfWork();
706
+            $uow->propertyChanged($object, $field, $oldValue, $value);
707
+        }
708
+    }
709
+}

+ 1 - 0
vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping.xsd 查看文件

@@ -0,0 +1 @@
1
+doctrine-extensions-mapping-2-2.xsd

+ 58 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/References/Fixture/ODM/MongoDB/Product.php 查看文件

@@ -0,0 +1,58 @@
1
+<?php
2
+
3
+namespace References\Fixture\ODM\MongoDB;
4
+
5
+use Doctrine\Common\Collections\Collection;
6
+use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
7
+use Gedmo\Mapping\Annotation as Gedmo;
8
+
9
+/**
10
+ * @ODM\Document
11
+ */
12
+class Product
13
+{
14
+    /**
15
+     * @ODM\Id
16
+     */
17
+    private $id;
18
+
19
+    /**
20
+     * @ODM\String
21
+     */
22
+    private $name;
23
+
24
+    /**
25
+     * @Gedmo\ReferenceMany(type="entity", class="References\Fixture\ORM\StockItem", mappedBy="product")
26
+     */
27
+    private $stockItems;
28
+
29
+    public function getId()
30
+    {
31
+        return $this->id;
32
+    }
33
+
34
+    public function setId($id)
35
+    {
36
+        $this->id = $id;
37
+    }
38
+
39
+    public function getName()
40
+    {
41
+        return $this->name;
42
+    }
43
+
44
+    public function setName($name)
45
+    {
46
+        $this->name = $name;
47
+    }
48
+
49
+    public function getStockItems()
50
+    {
51
+        return $this->stockItems;
52
+    }
53
+
54
+    public function setStockItems(Collection $stockItems)
55
+    {
56
+        $this->stockItems = $stockItems;
57
+    }
58
+}

+ 105 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/References/Fixture/ORM/StockItem.php 查看文件

@@ -0,0 +1,105 @@
1
+<?php
2
+
3
+namespace References\Fixture\ORM;
4
+
5
+use Doctrine\ORM\Mapping as ORM;
6
+use Gedmo\Mapping\Annotation as Gedmo;
7
+use References\Fixture\ODM\MongoDB\Product;
8
+
9
+/**
10
+ * @ORM\Entity
11
+ */
12
+class StockItem
13
+{
14
+    /**
15
+     * @ORM\Id
16
+     * @ORM\Column(type="integer")
17
+     * @ORM\GeneratedValue(strategy="IDENTITY")
18
+     */
19
+    private $id;
20
+
21
+    /**
22
+     * @ORM\Column
23
+     */
24
+    private $name;
25
+
26
+    /**
27
+     * @ORM\Column
28
+     */
29
+    private $sku;
30
+
31
+    /**
32
+     * @ORM\Column(type="integer")
33
+     */
34
+    private $quantity;
35
+
36
+    /**
37
+     * @Gedmo\ReferenceOne(type="document", class="References\Fixture\ODM\MongoDB\Product", inversedBy="stockItems", identifier="productId")
38
+     */
39
+    private $product;
40
+
41
+    /**
42
+     * @ORM\Column(type="string")
43
+     */
44
+    private $productId;
45
+
46
+    public function getId()
47
+    {
48
+        return $this->id;
49
+    }
50
+
51
+    public function setId($id)
52
+    {
53
+        $this->id = $id;
54
+    }
55
+
56
+    public function getName()
57
+    {
58
+        return $this->name;
59
+    }
60
+
61
+    public function setName($name)
62
+    {
63
+        $this->name = $name;
64
+    }
65
+
66
+    public function getSku()
67
+    {
68
+        return $this->sku;
69
+    }
70
+
71
+    public function setSku($sku)
72
+    {
73
+        $this->sku = $sku;
74
+    }
75
+
76
+    public function getQuantity()
77
+    {
78
+        return $this->quantity;
79
+    }
80
+
81
+    public function setQuantity($quantity)
82
+    {
83
+        $this->quantity = $quantity;
84
+    }
85
+
86
+    public function setProduct(Product $product)
87
+    {
88
+        $this->product = $product;
89
+    }
90
+
91
+    public function getProduct()
92
+    {
93
+        return $this->product;
94
+    }
95
+
96
+    public function setProductId($productId)
97
+    {
98
+        $this->productId = $productId;
99
+    }
100
+
101
+    public function getProductId()
102
+    {
103
+        return $this->productId;
104
+    }
105
+}

+ 123 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/References/ReferencesListenerTest.php 查看文件

@@ -0,0 +1,123 @@
1
+<?php
2
+
3
+namespace Gedmo\References;
4
+
5
+use Doctrine\Common\Annotations\AnnotationReader;
6
+use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver as MongoDBAnnotationDriver;
7
+use Doctrine\ODM\MongoDB\Configuration;
8
+use Doctrine\ORM\Mapping\Driver\AnnotationDriver as ORMAnnotationDriver;
9
+use References\Fixture\ODM\MongoDB\Product;
10
+use References\Fixture\ORM\StockItem;
11
+use Tool\BaseTestCaseOM;
12
+
13
+class ReferencesListenerTest extends BaseTestCaseOM
14
+{
15
+    private $em;
16
+    private $dm;
17
+
18
+    protected function setUp()
19
+    {
20
+        parent::setUp();
21
+
22
+        $reader = new AnnotationReader();
23
+
24
+        $this->dm = $this->getMockDocumentManager('test', new MongoDBAnnotationDriver($reader, __DIR__ . '/Fixture/ODM/MongoDB'));
25
+
26
+        $listener = new ReferencesListener(array(
27
+            'document' => $this->dm
28
+        ));
29
+
30
+        $this->evm->addEventSubscriber($listener);
31
+
32
+        $reader = new AnnotationReader();
33
+
34
+        $this->em = $this->getMockSqliteEntityManager(array('References\Fixture\ORM\StockItem'), new ORMAnnotationDriver($reader, __DIR__ . '/Fixture/ORM'));
35
+
36
+        $listener->registerManager('entity', $this->em);
37
+    }
38
+
39
+    public function testShouldPersistReferencedIdentifiersIntoIdentifierField()
40
+    {
41
+        $stockItem = new StockItem();
42
+        $stockItem->setName('Apple TV');
43
+        $stockItem->setSku('APP-TV');
44
+        $stockItem->setQuantity(25);
45
+
46
+        $product = new Product();
47
+        $product->setName('Apple TV');
48
+
49
+        $this->dm->persist($product);
50
+        $this->dm->flush();
51
+
52
+        $stockItem->setProduct($product);
53
+
54
+        $this->em->persist($stockItem);
55
+
56
+        $this->assertEquals($product->getId(), $stockItem->getProductId());
57
+    }
58
+
59
+    public function testShouldPopulateReferenceOneWithProxyFromIdentifierField()
60
+    {
61
+        $product = new Product();
62
+        $product->setName('Apple TV');
63
+
64
+        $this->dm->persist($product);
65
+        $this->dm->flush();
66
+
67
+        $stockItem = new StockItem();
68
+        $stockItem->setName('Apple TV');
69
+        $stockItem->setSku('APP-TV');
70
+        $stockItem->setQuantity(25);
71
+        $stockItem->setProductId($product->getId());
72
+
73
+        $this->em->persist($stockItem);
74
+        $this->em->flush();
75
+        $this->em->clear();
76
+
77
+        $stockItem = $this->em->find(get_class($stockItem), $stockItem->getId());
78
+
79
+        $this->assertSame($product, $stockItem->getProduct());
80
+    }
81
+
82
+    public function testShouldPopulateReferenceManyWithLazyCollectionInstance()
83
+    {
84
+        $product = new Product();
85
+        $product->setName('Apple TV');
86
+
87
+        $this->dm->persist($product);
88
+        $this->dm->flush();
89
+        $this->dm->clear();
90
+
91
+        $stockItem = new StockItem();
92
+        $stockItem->setName('Apple TV');
93
+        $stockItem->setSku('APP-TV');
94
+        $stockItem->setQuantity(25);
95
+        $stockItem->setProductId($product->getId());
96
+
97
+        $this->em->persist($stockItem);
98
+
99
+        $stockItem = new StockItem();
100
+        $stockItem->setName('Apple TV');
101
+        $stockItem->setSku('AMZN-APP-TV');
102
+        $stockItem->setQuantity(25);
103
+        $stockItem->setProductId($product->getId());
104
+
105
+        $this->em->persist($stockItem);
106
+        $this->em->flush();
107
+
108
+        $product = $this->dm->find(get_class($product), $product->getId());
109
+
110
+        $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $product->getStockItems());
111
+        $this->assertEquals(2, $product->getStockItems()->count());
112
+
113
+        $first = $product->getStockItems()->first();
114
+
115
+        $this->assertInstanceOf(get_class($stockItem), $first);
116
+        $this->assertEquals('APP-TV', $first->getSku());
117
+
118
+        $last = $product->getStockItems()->last();
119
+
120
+        $this->assertInstanceOf(get_class($stockItem), $last);
121
+        $this->assertEquals('AMZN-APP-TV', $last->getSku());
122
+    }
123
+}

+ 331 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/Tool/BaseTestCaseOM.php 查看文件

@@ -0,0 +1,331 @@
1
+<?php
2
+
3
+namespace Tool;
4
+
5
+// common
6
+use Doctrine\Common\Annotations\AnnotationReader;
7
+use Doctrine\Common\EventManager;
8
+use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
9
+// orm specific
10
+use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
11
+use Doctrine\ORM\Mapping\DefaultNamingStrategy;
12
+use Doctrine\ORM\Mapping\Driver\Driver as MappingDriverORM;
13
+use Doctrine\ORM\Mapping\Driver\AnnotationDriver as AnnotationDriverORM;
14
+use Doctrine\ORM\EntityManager;
15
+use Doctrine\ORM\Tools\SchemaTool;
16
+// odm specific
17
+use Doctrine\ODM\MongoDB\Mapping\Driver\Driver as MappingDriverODM;
18
+use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver as AnnotationDriverODM;
19
+use Doctrine\ODM\MongoDB\DocumentManager;
20
+use Doctrine\MongoDB\Connection;
21
+// listeners
22
+use Gedmo\Translatable\TranslatableListener;
23
+use Gedmo\Sluggable\SluggableListener;
24
+use Gedmo\Tree\TreeListener;
25
+use Gedmo\Timestampable\TimestampableListener;
26
+use Gedmo\Loggable\LoggableListener;
27
+
28
+/**
29
+ * Base test case contains common mock objects
30
+ * generation methods for multi object manager
31
+ * test cases
32
+ *
33
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
34
+ * @package Gedmo
35
+ * @subpackage BaseTestCase
36
+ * @link http://www.gediminasm.org
37
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
38
+ */
39
+abstract class BaseTestCaseOM extends \PHPUnit_Framework_TestCase
40
+{
41
+    /**
42
+     * @var EventManager
43
+     */
44
+    protected $evm;
45
+
46
+    /**
47
+     * Initialized document managers
48
+     *
49
+     * @var array
50
+     */
51
+    private $dms = array();
52
+
53
+    /**
54
+     * {@inheritdoc}
55
+     */
56
+    protected function setUp()
57
+    {
58
+
59
+    }
60
+
61
+    /**
62
+     * {@inheritdoc}
63
+     */
64
+    protected function tearDown()
65
+    {
66
+        foreach ($this->dms as $dm) {
67
+            if ($dm) {
68
+                foreach ($dm->getDocumentDatabases() as $db) {
69
+                    foreach ($db->listCollections() as $collection) {
70
+                        $collection->drop();
71
+                    }
72
+                }
73
+                $dm->getConnection()->close();
74
+                $dm = null;
75
+            }
76
+        }
77
+    }
78
+
79
+    /**
80
+     * DocumentManager mock object together with
81
+     * annotation mapping driver and database
82
+     *
83
+     * @param string $dbName
84
+     * @param Doctrine\ODM\MongoDB\Mapping\Driver\Driver $mappingDriver
85
+     * @return DocumentManager
86
+     */
87
+    protected function getMockDocumentManager($dbName, MappingDriver $mappingDriver = null)
88
+    {
89
+        if (!class_exists('Mongo')) {
90
+            $this->markTestSkipped('Missing Mongo extension.');
91
+        }
92
+        $conn = new Connection;
93
+        $config = $this->getMockAnnotatedODMMongoDBConfig($dbName, $mappingDriver);
94
+
95
+        $dm = null;
96
+        try {
97
+            $dm = DocumentManager::create($conn, $config, $this->getEventManager());
98
+            $dm->getConnection()->connect();
99
+        } catch (\MongoException $e) {
100
+            $this->markTestSkipped('Doctrine MongoDB ODM failed to connect');
101
+        }
102
+        return $dm;
103
+    }
104
+
105
+    /**
106
+     * DocumentManager mock object with
107
+     * annotation mapping driver
108
+     *
109
+     * @param string $dbName
110
+     * @param Doctrine\ODM\MongoDB\Mapping\Driver\Driver $mappingDriver
111
+     * @return DocumentManager
112
+     */
113
+    protected function getMockMappedDocumentManager($dbName, MappingDriver $mappingDriver = null)
114
+    {
115
+        $conn = $this->getMock('Doctrine\\MongoDB\\Connection');
116
+        $config = $this->getMockAnnotatedODMMongoDBConfig($dbName, $mappingDriver);
117
+
118
+        $dm = DocumentManager::create($conn, $config, $this->getEventManager());
119
+        return $dm;
120
+    }
121
+
122
+    /**
123
+     * EntityManager mock object together with
124
+     * annotation mapping driver and pdo_sqlite
125
+     * database in memory
126
+     *
127
+     * @param array $fixtures
128
+     * @param Doctrine\ORM\Mapping\Driver\Driver $mappingDriver
129
+     * @return EntityManager
130
+     */
131
+    protected function getMockSqliteEntityManager(array $fixtures, MappingDriver $mappingDriver = null)
132
+    {
133
+        $conn = array(
134
+            'driver' => 'pdo_sqlite',
135
+            'memory' => true,
136
+        );
137
+
138
+        $config = $this->getMockAnnotatedORMConfig($mappingDriver);
139
+        $em = EntityManager::create($conn, $config, $this->getEventManager());
140
+
141
+        $schema = array_map(function($class) use ($em) {
142
+            return $em->getClassMetadata($class);
143
+        }, $fixtures);
144
+
145
+        $schemaTool = new SchemaTool($em);
146
+        $schemaTool->dropSchema(array());
147
+        $schemaTool->createSchema($schema);
148
+        return $em;
149
+    }
150
+
151
+    /**
152
+     * EntityManager mock object with
153
+     * annotation mapping driver
154
+     *
155
+     * @param Doctrine\ORM\Mapping\Driver\Driver $mappingDriver
156
+     * @return EntityManager
157
+     */
158
+    protected function getMockMappedEntityManager(MappingDriver $mappingDriver = null)
159
+    {
160
+        $driver = $this->getMock('Doctrine\DBAL\Driver');
161
+        $driver->expects($this->once())
162
+            ->method('getDatabasePlatform')
163
+            ->will($this->returnValue($this->getMock('Doctrine\DBAL\Platforms\MySqlPlatform')));
164
+
165
+        $conn = $this->getMock('Doctrine\DBAL\Connection', array(), array(array(), $driver));
166
+        $conn->expects($this->once())
167
+            ->method('getEventManager')
168
+            ->will($this->returnValue($this->getEventManager()));
169
+
170
+        $config = $this->getMockAnnotatedORMConfig($mappingDriver);
171
+        $em = EntityManager::create($conn, $config);
172
+        return $em;
173
+    }
174
+
175
+    /**
176
+     * Creates default mapping driver
177
+     *
178
+     * @return \Doctrine\ORM\Mapping\Driver\Driver
179
+     */
180
+    protected function getDefaultORMMetadataDriverImplementation()
181
+    {
182
+        return new AnnotationDriverORM($_ENV['annotation_reader']);
183
+    }
184
+
185
+    /**
186
+     * Creates default mapping driver
187
+     *
188
+     * @return \Doctrine\ODM\MongoDB\Mapping\Driver\Driver
189
+     */
190
+    protected function getDefaultMongoODMMetadataDriverImplementation()
191
+    {
192
+        return new AnnotationDriverODM($_ENV['annotation_reader']);
193
+    }
194
+
195
+    /**
196
+     * Build event manager
197
+     *
198
+     * @return EventManager
199
+     */
200
+    private function getEventManager()
201
+    {
202
+        if (null === $this->evm) {
203
+            $this->evm = new EventManager;
204
+            $this->evm->addEventSubscriber(new TreeListener);
205
+            $this->evm->addEventSubscriber(new SluggableListener);
206
+            $this->evm->addEventSubscriber(new LoggableListener);
207
+            $this->evm->addEventSubscriber(new TranslatableListener);
208
+            $this->evm->addEventSubscriber(new TimestampableListener);
209
+        }
210
+
211
+        return $this->evm;
212
+    }
213
+
214
+    /**
215
+     * Get annotation mapping configuration
216
+     *
217
+     * @param string $dbName
218
+     * @param Doctrine\ODM\MongoDB\Mapping\Driver\Driver $mappingDriver
219
+     * @return Doctrine\ORM\Configuration
220
+     */
221
+    private function getMockAnnotatedODMMongoDBConfig($dbName, MappingDriver $mappingDriver = null)
222
+    {
223
+        $config = $this->getMock('Doctrine\\ODM\\MongoDB\\Configuration');
224
+        $config->expects($this->once())
225
+            ->method('getProxyDir')
226
+            ->will($this->returnValue(__DIR__.'/../../temp'));
227
+
228
+        $config->expects($this->once())
229
+            ->method('getProxyNamespace')
230
+            ->will($this->returnValue('Proxy'));
231
+
232
+        $config->expects($this->once())
233
+            ->method('getHydratorDir')
234
+            ->will($this->returnValue(__DIR__.'/../../temp'));
235
+
236
+        $config->expects($this->once())
237
+            ->method('getHydratorNamespace')
238
+            ->will($this->returnValue('Hydrator'));
239
+
240
+        $config->expects($this->any())
241
+            ->method('getDefaultDB')
242
+            ->will($this->returnValue($dbName));
243
+
244
+        $config->expects($this->once())
245
+            ->method('getAutoGenerateProxyClasses')
246
+            ->will($this->returnValue(true));
247
+
248
+        $config->expects($this->once())
249
+            ->method('getAutoGenerateHydratorClasses')
250
+            ->will($this->returnValue(true));
251
+
252
+        $config->expects($this->once())
253
+            ->method('getClassMetadataFactoryName')
254
+            ->will($this->returnValue('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadataFactory'));
255
+
256
+        $config
257
+            ->expects($this->any())
258
+            ->method('getMongoCmd')
259
+            ->will($this->returnValue('$'))
260
+        ;
261
+
262
+        $config
263
+            ->expects($this->any())
264
+            ->method('getDefaultCommitOptions')
265
+            ->will($this->returnValue(array('safe' => true)))
266
+        ;
267
+
268
+        if (null === $mappingDriver) {
269
+            $mappingDriver = $this->getDefaultMongoODMMetadataDriverImplementation();
270
+        }
271
+
272
+        $config->expects($this->any())
273
+            ->method('getMetadataDriverImpl')
274
+            ->will($this->returnValue($mappingDriver));
275
+
276
+        return $config;
277
+    }
278
+
279
+    /**
280
+     * Get annotation mapping configuration for ORM
281
+     *
282
+     * @param Doctrine\ORM\Mapping\Driver\Driver $mappingDriver
283
+     * @return Doctrine\ORM\Configuration
284
+     */
285
+    private function getMockAnnotatedORMConfig(MappingDriver $mappingDriver = null)
286
+    {
287
+        $config = $this->getMock('Doctrine\ORM\Configuration');
288
+        $config->expects($this->once())
289
+            ->method('getProxyDir')
290
+            ->will($this->returnValue(__DIR__.'/../../temp'));
291
+
292
+        $config->expects($this->once())
293
+            ->method('getProxyNamespace')
294
+            ->will($this->returnValue('Proxy'));
295
+
296
+        $config->expects($this->once())
297
+            ->method('getAutoGenerateProxyClasses')
298
+            ->will($this->returnValue(true));
299
+
300
+        $config->expects($this->once())
301
+            ->method('getClassMetadataFactoryName')
302
+            ->will($this->returnValue('Doctrine\\ORM\\Mapping\\ClassMetadataFactory'));
303
+
304
+        $config
305
+            ->expects($this->any())
306
+            ->method('getDefaultRepositoryClassName')
307
+            ->will($this->returnValue('Doctrine\\ORM\\EntityRepository'))
308
+        ;
309
+
310
+        $config
311
+            ->expects($this->any())
312
+            ->method('getQuoteStrategy')
313
+            ->will($this->returnValue(new DefaultQuoteStrategy()))
314
+        ;
315
+
316
+        $config
317
+            ->expects($this->any())
318
+            ->method('getNamingStrategy')
319
+            ->will($this->returnValue(new DefaultNamingStrategy()))
320
+        ;
321
+        if (null === $mappingDriver) {
322
+            $mappingDriver = $this->getDefaultORMMetadataDriverImplementation();
323
+        }
324
+
325
+        $config->expects($this->any())
326
+            ->method('getMetadataDriverImpl')
327
+            ->will($this->returnValue($mappingDriver));
328
+
329
+        return $config;
330
+    }
331
+}

+ 108 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/Tree/Fixture/MPFeaturesCategory.php 查看文件

@@ -0,0 +1,108 @@
1
+<?php
2
+
3
+namespace Tree\Fixture;
4
+
5
+use Gedmo\Tree\Node as NodeInterface;
6
+use Gedmo\Mapping\Annotation as Gedmo;
7
+use Doctrine\ORM\Mapping as ORM;
8
+
9
+/**
10
+ * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\MaterializedPathRepository")
11
+ * @Gedmo\Tree(type="materializedPath")
12
+ */
13
+class MPFeaturesCategory
14
+{
15
+    /**
16
+     * @ORM\Column(name="id", type="integer")
17
+     * @ORM\Id
18
+     * @ORM\GeneratedValue
19
+     */
20
+    private $id;
21
+
22
+    /**
23
+     * @Gedmo\TreePath(appendId=false, startsWithSeparator=true, endsWithSeparator=false)
24
+     * @ORM\Column(name="path", type="string", length=3000, nullable=true)
25
+     */
26
+    private $path;
27
+
28
+    /**
29
+     * @Gedmo\TreePathHash
30
+     * @ORM\Column(name="pathhash", type="string", length=32, nullable=true)
31
+     */
32
+    private $pathHash;
33
+
34
+    /**
35
+     * @Gedmo\TreePathSource
36
+     * @ORM\Column(name="title", type="string", length=64)
37
+     */
38
+    private $title;
39
+
40
+    /**
41
+     * @Gedmo\TreeParent
42
+     * @ORM\ManyToOne(targetEntity="MPFeaturesCategory", inversedBy="children")
43
+     * @ORM\JoinColumns({
44
+     *   @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL")
45
+     * })
46
+     */
47
+    private $parentId;
48
+
49
+    /**
50
+     * @Gedmo\TreeLevel
51
+     * @ORM\Column(name="lvl", type="integer", nullable=true)
52
+     */
53
+    private $level;
54
+
55
+    /**
56
+     * @ORM\OneToMany(targetEntity="MPFeaturesCategory", mappedBy="parent")
57
+     */
58
+    private $children;
59
+
60
+    /**
61
+     * @ORM\OneToMany(targetEntity="Article", mappedBy="category")
62
+     */
63
+    private $comments;
64
+
65
+    public function getId()
66
+    {
67
+        return $this->id;
68
+    }
69
+
70
+    public function setTitle($title)
71
+    {
72
+        $this->title = $title;
73
+    }
74
+
75
+    public function getTitle()
76
+    {
77
+        return $this->title;
78
+    }
79
+
80
+    public function setParent(MPFeaturesCategory $parent = null)
81
+    {
82
+        $this->parentId = $parent;
83
+    }
84
+
85
+    public function getParent()
86
+    {
87
+        return $this->parentId;
88
+    }
89
+
90
+    public function setPath($path)
91
+    {
92
+        $this->path = $path;
93
+    }
94
+
95
+    public function getPath()
96
+    {
97
+        return $this->path;
98
+    }
99
+
100
+    public function getLevel()
101
+    {
102
+        return $this->level;
103
+    }
104
+
105
+    public function getPathHash(){
106
+        return $this->pathHash;
107
+    }
108
+}

+ 109 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/Tree/MaterializedPathORMFeaturesTest.php 查看文件

@@ -0,0 +1,109 @@
1
+<?php
2
+
3
+namespace Gedmo\Tree;
4
+
5
+use Doctrine\Common\EventManager;
6
+use Tool\BaseTestCaseORM;
7
+
8
+/**
9
+ * These are tests for Tree behavior
10
+ *
11
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
12
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
13
+ * @package Gedmo.Tree
14
+ * @link http://www.gediminasm.org
15
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
16
+ */
17
+class MaterializedPathORMFeaturesTest extends BaseTestCaseORM
18
+{
19
+    const CATEGORY = "Tree\\Fixture\\MPFeaturesCategory";
20
+
21
+    protected $config;
22
+    protected $listener;
23
+
24
+    protected function setUp()
25
+    {
26
+        parent::setUp();
27
+
28
+        $this->listener = new TreeListener;
29
+
30
+        $evm = new EventManager;
31
+        $evm->addEventSubscriber($this->listener);
32
+
33
+        $this->getMockSqliteEntityManager($evm);
34
+
35
+        $meta = $this->em->getClassMetadata(self::CATEGORY);
36
+        $this->config = $this->listener->getConfiguration($this->em, $meta->name);
37
+    }
38
+
39
+    /**
40
+     * @test
41
+     */
42
+    function checkPathsAndHash()
43
+    {
44
+        $category = $this->createCategory();
45
+        $category->setTitle('1');
46
+        $category2 = $this->createCategory();
47
+        $category2->setTitle('2');
48
+        $category3 = $this->createCategory();
49
+        $category3->setTitle('3');
50
+        $category4 = $this->createCategory();
51
+        $category4->setTitle('4');
52
+
53
+        $category2->setParent($category);
54
+        $category3->setParent($category2);
55
+
56
+        $this->em->persist($category4);
57
+        $this->em->persist($category3);
58
+        $this->em->persist($category2);
59
+        $this->em->persist($category);
60
+        $this->em->flush();
61
+
62
+        $this->em->refresh($category);
63
+        $this->em->refresh($category2);
64
+        $this->em->refresh($category3);
65
+        $this->em->refresh($category4);
66
+
67
+        $this->assertEquals($this->generatePath(array('1' => $category->getId())), $category->getPath());
68
+        $this->assertEquals($this->generatePath(array('1' => $category->getId(), '2' => $category2->getId())), $category2->getPath());
69
+        $this->assertEquals($this->generatePath(array('1' => $category->getId(), '2' => $category2->getId(), '3' => $category3->getId())), $category3->getPath());
70
+        $this->assertEquals($this->generatePath(array('4' => $category4->getId())), $category4->getPath());
71
+
72
+        $this->assertEquals($this->generatePathHash(array('1' => $category->getId())), $category->getPathHash());
73
+        $this->assertEquals($this->generatePathHash(array('1' => $category->getId(), '2' => $category2->getId())), $category2->getPathHash());
74
+        $this->assertEquals($this->generatePathHash(array('1' => $category->getId(), '2' => $category2->getId(), '3' => $category3->getId())), $category3->getPathHash());
75
+        $this->assertEquals($this->generatePathHash(array('4' => $category4->getId())), $category4->getPathHash());
76
+    }
77
+
78
+
79
+
80
+
81
+    public function createCategory()
82
+    {
83
+        $class = self::CATEGORY;
84
+        return new $class;
85
+    }
86
+
87
+    protected function getUsedEntityFixtures()
88
+    {
89
+        return array(
90
+            self::CATEGORY
91
+        );
92
+    }
93
+
94
+    public function generatePath(array $sources)
95
+    {
96
+        $path = '';
97
+        foreach ($sources as $p => $id) {
98
+            $path .= $this->config['path_separator'] . $p;
99
+        }
100
+
101
+        return $path;
102
+    }
103
+
104
+    public function generatePathHash(array $sources)
105
+    {
106
+
107
+        return md5($this->generatePath($sources));
108
+    }
109
+}

+ 27 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumericTest.php 查看文件

@@ -0,0 +1,27 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable;
4
+
5
+use Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorAlphanumeric;
6
+
7
+/**
8
+ * These are tests for FilenameGeneratorAlphanumeric class
9
+ *
10
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
11
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
12
+ * @package Gedmo.Uploadable.FilenameGenerator
13
+ * @link http://www.gediminasm.org
14
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
15
+ */
16
+class FilenameGeneratorAlphanumericTest extends \PHPUnit_Framework_TestCase
17
+{
18
+    public function testGenerator()
19
+    {
20
+        $generator = new FilenameGeneratorAlphanumeric();
21
+
22
+        $filename = 'MegaName_For_A_###$$$File$$$###';
23
+        $extension = '.exe';
24
+
25
+        $this->assertEquals('meganame-for-a-file-.exe', $generator->generate($filename, $extension));
26
+    }
27
+}

+ 698 - 0
vendor/gedmo/doctrine-extensions/tests/Gedmo/Uploadable/UploadableEntityTest.php 查看文件

@@ -0,0 +1,698 @@
1
+<?php
2
+
3
+namespace Gedmo\Uploadable;
4
+
5
+use Tool\BaseTestCaseORM,
6
+    Doctrine\Common\EventManager,
7
+    Doctrine\Common\Util\Debug,
8
+    Uploadable\Fixture\Entity\Image,
9
+    Uploadable\Fixture\Entity\Article,
10
+    Uploadable\Fixture\Entity\File,
11
+    Uploadable\Fixture\Entity\FileWithoutPath,
12
+    Uploadable\Fixture\Entity\FileWithSha1Name,
13
+    Uploadable\Fixture\Entity\FileWithAlphanumericName,
14
+    Uploadable\Fixture\Entity\FileWithCustomFilenameGenerator,
15
+    Uploadable\Fixture\Entity\FileAppendNumber,
16
+    Uploadable\Fixture\Entity\FileWithMaxSize,
17
+    Uploadable\Fixture\Entity\FileWithAllowedTypes,
18
+    Uploadable\Fixture\Entity\FileWithDisallowedTypes,
19
+    Gedmo\Uploadable\Stub\UploadableListenerStub,
20
+    Gedmo\Uploadable\Stub\MimeTypeGuesserStub,
21
+    Gedmo\Uploadable\FileInfo\FileInfoArray;
22
+
23
+/**
24
+ * These are tests for Uploadable behavior
25
+ *
26
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
27
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
28
+ * @package Gedmo.Uploadable
29
+ * @link http://www.gediminasm.org
30
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
31
+ */
32
+class UploadableEntityTest extends BaseTestCaseORM
33
+{
34
+    const IMAGE_CLASS = 'Uploadable\Fixture\Entity\Image';
35
+    const ARTICLE_CLASS = 'Uploadable\Fixture\Entity\Article';
36
+    const FILE_CLASS = 'Uploadable\Fixture\Entity\File';
37
+    const FILE_APPEND_NUMBER_CLASS = 'Uploadable\Fixture\Entity\FileAppendNumber';
38
+    const FILE_WITHOUT_PATH_CLASS = 'Uploadable\Fixture\Entity\FileWithoutPath';
39
+    const FILE_WITH_SHA1_NAME_CLASS = 'Uploadable\Fixture\Entity\FileWithSha1Name';
40
+    const FILE_WITH_ALPHANUMERIC_NAME_CLASS = 'Uploadable\Fixture\Entity\FileWithAlphanumericName';
41
+    const FILE_WITH_CUSTOM_FILENAME_GENERATOR_CLASS = 'Uploadable\Fixture\Entity\FileWithCustomFilenameGenerator';
42
+    const FILE_WITH_MAX_SIZE_CLASS = 'Uploadable\Fixture\Entity\FileWithMaxSize';
43
+    const FILE_WITH_ALLOWED_TYPES_CLASS = 'Uploadable\Fixture\Entity\FileWithAllowedTypes';
44
+    const FILE_WITH_DISALLOWED_TYPES_CLASS = 'Uploadable\Fixture\Entity\FileWithDisallowedTypes';
45
+
46
+    private $listener;
47
+    private $testFile;
48
+    private $testFile2;
49
+    private $testFile3;
50
+    private $testFileWithoutExt;
51
+    private $testFileWithSpaces;
52
+    private $destinationTestDir;
53
+    private $destinationTestFile;
54
+    private $destinationTestFile2;
55
+    private $destinationTestFile3;
56
+    private $destinationTestFileWithoutExt;
57
+    private $destinationTestFileWithspaces;
58
+    private $testFilename;
59
+    private $testFilename2;
60
+    private $testFilename3;
61
+    private $testFilenameWithoutExt;
62
+    private $testFilenameWithSpaces;
63
+    private $testFileSize;
64
+    private $testFileMimeType;
65
+
66
+    protected function setUp()
67
+    {
68
+        parent::setUp();
69
+
70
+        $evm = new EventManager;
71
+        $this->listener = new UploadableListenerStub();
72
+        $this->listener->setMimeTypeGuesser(new MimeTypeGuesserStub('text/plain'));
73
+
74
+        $evm->addEventSubscriber($this->listener);
75
+        $config = $this->getMockAnnotatedConfig();
76
+        $this->em = $this->getMockSqliteEntityManager($evm, $config);
77
+        $this->testFile = __DIR__.'/../../data/test.txt';
78
+        $this->testFile2 = __DIR__.'/../../data/test2.txt';
79
+        $this->testFile3 = __DIR__.'/../../data/test_3.txt';
80
+        $this->testFileWithoutExt = __DIR__.'/../../data/test4';
81
+        $this->testFileWithSpaces = __DIR__.'/../../data/test with spaces.txt';
82
+        $this->destinationTestDir = __DIR__.'/../../temp/uploadable';
83
+        $this->destinationTestFile = $this->destinationTestDir.'/test.txt';
84
+        $this->destinationTestFile2 = $this->destinationTestDir.'/test2.txt';
85
+        $this->destinationTestFile3 = $this->destinationTestDir.'/test_3.txt';
86
+        $this->destinationTestFileWithoutExt = $this->destinationTestDir.'/test4';
87
+        $this->destinationTestFileWithSpaces = $this->destinationTestDir.'/test with spaces';
88
+        $this->testFilename = substr($this->testFile, strrpos($this->testFile, '/') + 1);
89
+        $this->testFilename2 = substr($this->testFile2, strrpos($this->testFile2, '/') + 1);
90
+        $this->testFilename3 = substr($this->testFile3, strrpos($this->testFile3, '/') + 1);
91
+        $this->testFilenameWithoutExt = substr($this->testFileWithoutExt, strrpos($this->testFileWithoutExt, '/') + 1);
92
+        $this->testFilenameWithSpaces= substr($this->testFileWithSpaces, strrpos($this->testFileWithSpaces, '/') + 1);
93
+        $this->testFileSize = 4;
94
+        $this->testFileMimeType = 'text/plain';
95
+
96
+        $this->clearFilesAndDirectories();
97
+
98
+        if (!is_dir($this->destinationTestDir)) {
99
+            mkdir($this->destinationTestDir);
100
+        };
101
+    }
102
+
103
+    public function tearDown()
104
+    {
105
+        $this->clearFilesAndDirectories();
106
+    }
107
+
108
+    public function testUploadableEntity()
109
+    {
110
+        // INSERTION of an Uploadable Entity
111
+
112
+        // If there was no uploaded file, we do nothing
113
+        $image = new Image();
114
+        $image->setTitle('123');
115
+
116
+        $this->em->persist($image);
117
+        $this->em->flush();
118
+
119
+        $this->assertNull($image->getFilePath());
120
+
121
+        // If there is an uploaded file, we process it
122
+        $fileInfo = $this->generateUploadedFile();
123
+
124
+        $image2 = new Image();
125
+        $image2->setTitle('456');
126
+        $this->listener->addEntityFileInfo($image2, $fileInfo);
127
+
128
+        $this->em->persist($image2);
129
+        $this->em->flush();
130
+
131
+        $this->em->refresh($image2);
132
+
133
+        // We need to set this again because of the recent refresh
134
+        $firstFile = $image2->getFilePath();
135
+
136
+        $this->assertPathEquals($image2->getPath().'/'.$fileInfo['name'], $image2->getFilePath());
137
+        $this->assertTrue(is_file($firstFile));
138
+        $this->assertEquals($fileInfo['size'], $image2->getSize());
139
+        $this->assertEquals($fileInfo['type'], $image2->getMime());
140
+
141
+        // UPDATE of an Uploadable Entity
142
+
143
+        // We change the "uploaded" file
144
+        $fileInfo['tmp_name'] = $this->testFile2;
145
+        $fileInfo['name'] = $this->testFilename2;
146
+
147
+        // We use a FileInfoInterface instance here
148
+        $this->listener->addEntityFileInfo($image2, new FileInfoArray($fileInfo));
149
+
150
+        $this->em->flush();
151
+
152
+        $this->em->refresh($image2);
153
+
154
+        $lastFile = $image2->getFilePath();
155
+
156
+        $this->assertPathEquals($image2->getPath().'/'.$fileInfo['name'], $image2->getFilePath());
157
+        $this->assertTrue(is_file($lastFile));
158
+
159
+        // First file should be removed on update
160
+        $this->assertFalse(is_file($firstFile));
161
+
162
+        // REMOVAL of an Uploadable Entity
163
+        $this->em->remove($image2);
164
+        $this->em->flush();
165
+
166
+        $this->assertFalse(is_file($lastFile));
167
+    }
168
+
169
+    public function testEntityWithUploadableEntities()
170
+    {
171
+        $artRepo = $this->em->getRepository(self::ARTICLE_CLASS);
172
+        $article = new Article();
173
+        $article->setTitle('Test');
174
+
175
+        $file1 = new File();
176
+        $file2 = new File();
177
+        $file3 = new File();
178
+
179
+        $article->addFile($file1);
180
+        $article->addFile($file2);
181
+        $article->addFile($file3);
182
+
183
+        $filesArrayIndex = 'file';
184
+
185
+        $fileInfo = $this->generateUploadedFile($filesArrayIndex);
186
+        $fileInfo2 = $this->generateUploadedFile($filesArrayIndex);
187
+        $fileInfo3 = $this->generateUploadedFile($filesArrayIndex);
188
+
189
+        $this->listener->addEntityFileInfo($file1, $fileInfo);
190
+        $this->listener->addEntityFileInfo($file2, $fileInfo2);
191
+        $this->listener->addEntityFileInfo($file3, $fileInfo3);
192
+
193
+        $this->em->persist($article);
194
+
195
+        $this->em->flush();
196
+
197
+        $art = $artRepo->findOneByTitle('Test');
198
+        $files = $art->getFiles();
199
+        $file1Path = $file1->getPath().'/'.$fileInfo['name'];
200
+        $file2Path = $file2->getPath().'/'.$fileInfo['name'];
201
+        $file3Path = $file3->getPath().'/'.$fileInfo['name'];
202
+
203
+        $this->assertPathEquals($file1Path, $files[0]->getFilePath());
204
+        $this->assertPathEquals($file2Path, $files[1]->getFilePath());
205
+        $this->assertPathEquals($file3Path, $files[2]->getFilePath());
206
+    }
207
+
208
+    /**
209
+     * @expectedException Gedmo\Exception\UploadableNoPathDefinedException
210
+     */
211
+    public function testNoPathDefinedOnEntityOrListenerThrowsException()
212
+    {
213
+        $file = new FileWithoutPath();
214
+
215
+        $fileInfo = $this->generateUploadedFile();
216
+
217
+        $this->listener->addEntityFileInfo($file, $fileInfo);
218
+
219
+        $this->em->persist($file);
220
+        $this->em->flush();
221
+    }
222
+
223
+    public function testNoPathDefinedOnEntityButDefinedOnListenerUsesDefaultPath()
224
+    {
225
+        // We set the default path on the listener
226
+        $this->listener->setDefaultPath($this->destinationTestDir);
227
+
228
+        $file = new FileWithoutPath();
229
+        $fileInfo = $this->generateUploadedFile();
230
+
231
+        $this->listener->addEntityFileInfo($file, $fileInfo);
232
+
233
+        $this->em->persist($file);
234
+        $this->em->flush();
235
+
236
+        $this->em->refresh($file);
237
+
238
+        $this->assertPathEquals($this->destinationTestFile, $file->getFilePath());
239
+    }
240
+
241
+    public function testCallbackIsCalledIfItsSetOnEntity()
242
+    {
243
+        $file = new File();
244
+        $fileInfo = $this->generateUploadedFile();
245
+
246
+        $this->listener->addEntityFileInfo($file, $fileInfo);
247
+
248
+        $this->em->persist($file);
249
+        $this->em->flush();
250
+
251
+        $this->assertTrue($file->callbackWasCalled);
252
+    }
253
+
254
+    /**
255
+     * @dataProvider uploadExceptionsProvider
256
+     */
257
+    public function testUploadExceptions($error, $exceptionClass)
258
+    {
259
+        $this->setExpectedException($exceptionClass);
260
+
261
+        $file = new File();
262
+        $fileInfo = $this->generateUploadedFile();
263
+        $fileInfo['error'] = $error;
264
+
265
+        $this->listener->addEntityFileInfo($file, $fileInfo);
266
+
267
+        $this->em->persist($file);
268
+        $this->em->flush();
269
+    }
270
+
271
+    public function testSettingAnotherDefaultFileInfoClass()
272
+    {
273
+        $fileInfoStubClass = 'Gedmo\Uploadable\Stub\FileInfoStub';
274
+
275
+        $this->listener->setDefaultFileInfoClass($fileInfoStubClass);
276
+
277
+        $file = new File();
278
+        $fileInfo = $this->generateUploadedFile();
279
+
280
+        $this->listener->addEntityFileInfo($file, $fileInfo);
281
+        $fileInfo = $this->listener->getEntityFileInfo($file);
282
+
283
+        $this->assertInstanceOf($fileInfoStubClass, $fileInfo);
284
+    }
285
+
286
+    public function testFileWithFilenameSha1Generator()
287
+    {
288
+        $file = new FileWithSha1Name();
289
+        $fileInfo = $this->generateUploadedFile();
290
+
291
+        $this->listener->addEntityFileInfo($file, $fileInfo);
292
+
293
+        $this->em->persist($file);
294
+        $this->em->flush();
295
+
296
+        $this->em->refresh($file);
297
+
298
+        $sha1String = substr($file->getFilePath(), strrpos($file->getFilePath(), '/') + 1);
299
+        $sha1String = str_replace('.txt', '', $sha1String);
300
+
301
+        $this->assertRegExp('/[a-z0-9]{40}/', $sha1String);
302
+    }
303
+
304
+    public function testFileWithFilenameAlphanumericGenerator()
305
+    {
306
+        $file = new FileWithAlphanumericName();
307
+        $fileInfo = $this->generateUploadedFile('image', $this->testFile3, $this->testFilename3);
308
+
309
+        $this->listener->addEntityFileInfo($file, $fileInfo);
310
+
311
+        $this->em->persist($file);
312
+        $this->em->flush();
313
+
314
+        $this->em->refresh($file);
315
+
316
+        $filename = substr($file->getFilePath(), strrpos($file->getFilePath(), '/') + 1);
317
+
318
+        $this->assertEquals('test-3.txt', $filename);
319
+    }
320
+
321
+    public function testFileWithCustomFilenameGenerator()
322
+    {
323
+        $file = new FileWithCustomFilenameGenerator();
324
+        $fileInfo = $this->generateUploadedFile();
325
+
326
+        $this->listener->addEntityFileInfo($file, $fileInfo);
327
+
328
+        $this->em->persist($file);
329
+        $this->em->flush();
330
+
331
+        $this->em->refresh($file);
332
+
333
+        $filename = substr($file->getFilePath(), strrpos($file->getFilePath(), '/') + 1);
334
+
335
+        $this->assertEquals('123.txt', $filename);
336
+    }
337
+
338
+    public function testUploadFileWithoutExtension()
339
+    {
340
+        $file = new File();
341
+        $fileInfo = $this->generateUploadedFile('image', $this->testFileWithoutExt, $this->testFilenameWithoutExt);
342
+
343
+        $this->listener->addEntityFileInfo($file, $fileInfo);
344
+
345
+        $this->em->persist($file);
346
+        $this->em->flush();
347
+
348
+        $this->em->refresh($file);
349
+
350
+        $filePath = $file->getPath().'/'.$fileInfo['name'];
351
+
352
+        $this->assertPathEquals($filePath, $file->getFilePath());
353
+    }
354
+
355
+    /**
356
+     * @expectedException Gedmo\Exception\UploadableFileAlreadyExistsException
357
+     */
358
+    public function testFileAlreadyExistsException()
359
+    {
360
+        $file = new Image();
361
+        $file->setTitle('test');
362
+        $fileInfo = $this->generateUploadedFile('image', $this->testFileWithoutExt, $this->testFilenameWithoutExt);
363
+
364
+        $this->listener->addEntityFileInfo($file, $fileInfo);
365
+
366
+        $this->em->persist($file);
367
+        $this->em->flush();
368
+
369
+        $this->listener->addEntityFileInfo($file, $fileInfo);
370
+
371
+        $this->em->flush();
372
+    }
373
+
374
+    public function test_removeFile_ifItsNotAFileThenReturnFalse()
375
+    {
376
+        $this->assertFalse($this->listener->removeFile('non_existent_file'));
377
+    }
378
+
379
+    public function test_moveFile_usingAppendNumberOptionAppendsNumberToFilenameIfItAlreadyExists()
380
+    {
381
+        $file = new FileAppendNumber();
382
+        $file2 = new FileAppendNumber();
383
+
384
+        $file->setTitle('test');
385
+        $file2->setTitle('test2');
386
+
387
+        $fileInfo = $this->generateUploadedFile();
388
+
389
+        $this->listener->addEntityFileInfo($file, $fileInfo);
390
+
391
+        $this->em->persist($file);
392
+        $this->em->flush();
393
+
394
+        $this->listener->addEntityFileInfo($file2, $fileInfo);
395
+
396
+        $this->em->persist($file2);
397
+        $this->em->flush();
398
+
399
+        $this->em->refresh($file2);
400
+
401
+        $filename = substr($file2->getFilePath(), strrpos($file2->getFilePath(), '/') + 1);
402
+
403
+        $this->assertEquals('test-2.txt', $filename);
404
+    }
405
+
406
+    /**
407
+     * @expectedException Gedmo\Exception\UploadableUploadException
408
+     */
409
+    public function test_moveFile_ifUploadedFileCantBeMovedThrowException()
410
+    {
411
+        $this->listener->returnFalseOnMoveUploadedFile = true;
412
+
413
+        $file = new Image();
414
+        $file->setTitle('test');
415
+        $fileInfo = $this->generateUploadedFile();
416
+
417
+        $this->listener->addEntityFileInfo($file, $fileInfo);
418
+
419
+        $this->em->persist($file);
420
+        $this->em->flush();
421
+    }
422
+
423
+    /**
424
+     * @expectedException RuntimeException
425
+     */
426
+    public function test_addEntityFileInfo_ifFileInfoIsNotValidThrowException()
427
+    {
428
+        $this->listener->addEntityFileInfo(new Image, 'invalidFileInfo');
429
+    }
430
+
431
+    /**
432
+     * @expectedException RuntimeException
433
+     */
434
+    public function test_getEntityFileInfo_ifTheresNoFileInfoForEntityThrowException()
435
+    {
436
+        $this->listener->getEntityFileInfo(new Image);
437
+    }
438
+
439
+    /**
440
+     * @expectedException Gedmo\Exception\UploadableMaxSizeException
441
+     */
442
+    public function test_fileExceedingMaximumAllowedSizeThrowsException()
443
+    {
444
+        // We set the default path on the listener
445
+        $this->listener->setDefaultPath($this->destinationTestDir);
446
+
447
+        $file = new FileWithMaxSize();
448
+        $fileInfo = $this->generateUploadedFile();
449
+
450
+        $this->listener->addEntityFileInfo($file, $fileInfo);
451
+
452
+        $this->em->persist($file);
453
+        $this->em->flush();
454
+    }
455
+
456
+    public function test_fileNotExceedingMaximumAllowedSizeDoesntThrowException()
457
+    {
458
+        // We set the default path on the listener
459
+        $this->listener->setDefaultPath($this->destinationTestDir);
460
+
461
+        $file = new FileWithMaxSize();
462
+        $size = 0.0001;
463
+        $fileInfo = $this->generateUploadedFile('image', false, false, array('size' => $size));
464
+
465
+        $this->listener->addEntityFileInfo($file, $fileInfo);
466
+
467
+        $this->em->persist($file);
468
+        $this->em->flush();
469
+
470
+        $this->em->refresh($file);
471
+
472
+        $this->assertEquals($size, $file->getFileSize());
473
+    }
474
+
475
+    /**
476
+     * @expectedException Gedmo\Exception\UploadableCouldntGuessMimeTypeException
477
+     */
478
+    public function test_ifMimeTypeGuesserCantResolveTypeThrowException()
479
+    {
480
+        // We set the default path on the listener
481
+        $this->listener->setDefaultPath($this->destinationTestDir);
482
+        $this->listener->setMimeTypeGuesser(new MimeTypeGuesserStub(null));
483
+
484
+        $file = new FileWithAllowedTypes();
485
+        $fileInfo = $this->generateUploadedFile();
486
+
487
+        $this->listener->addEntityFileInfo($file, $fileInfo);
488
+
489
+        $this->em->persist($file);
490
+        $this->em->flush();
491
+    }
492
+
493
+    /**
494
+     * @expectedException Gedmo\Exception\UploadableInvalidMimeTypeException
495
+     */
496
+    public function test_allowedTypesOption_ifMimeTypeIsInvalidThrowException()
497
+    {
498
+        // We set the default path on the listener
499
+        $this->listener->setDefaultPath($this->destinationTestDir);
500
+        $this->listener->setMimeTypeGuesser(new MimeTypeGuesserStub('text/css'));
501
+
502
+        $file = new FileWithAllowedTypes();
503
+        $fileInfo = $this->generateUploadedFile();
504
+
505
+        $this->listener->addEntityFileInfo($file, $fileInfo);
506
+
507
+        $this->em->persist($file);
508
+        $this->em->flush();
509
+    }
510
+
511
+    public function test_allowedTypesOption_ifMimeTypeIsValidThenDontThrowException()
512
+    {
513
+        // We set the default path on the listener
514
+        $this->listener->setDefaultPath($this->destinationTestDir);
515
+
516
+        $file = new FileWithAllowedTypes();
517
+        $fileInfo = $this->generateUploadedFile();
518
+
519
+        $this->listener->addEntityFileInfo($file, $fileInfo);
520
+
521
+        $this->em->persist($file);
522
+        $this->em->flush();
523
+    }
524
+
525
+    /**
526
+     * @expectedException Gedmo\Exception\UploadableInvalidMimeTypeException
527
+     */
528
+    public function test_disallowedTypesOption_ifMimeTypeIsInvalidThrowException()
529
+    {
530
+        // We set the default path on the listener
531
+        $this->listener->setDefaultPath($this->destinationTestDir);
532
+        $this->listener->setMimeTypeGuesser(new MimeTypeGuesserStub('text/css'));
533
+
534
+        $file = new FileWithDisallowedTypes();
535
+        $fileInfo = $this->generateUploadedFile();
536
+
537
+        $this->listener->addEntityFileInfo($file, $fileInfo);
538
+
539
+        $this->em->persist($file);
540
+        $this->em->flush();
541
+    }
542
+
543
+    public function test_disallowedTypesOption_ifMimeTypeIsValidThenDontThrowException()
544
+    {
545
+        // We set the default path on the listener
546
+        $this->listener->setDefaultPath($this->destinationTestDir);
547
+        $this->listener->setMimeTypeGuesser(new MimeTypeGuesserStub('video/jpeg'));
548
+
549
+        $file = new FileWithDisallowedTypes();
550
+        $fileInfo = $this->generateUploadedFile();
551
+
552
+        $this->listener->addEntityFileInfo($file, $fileInfo);
553
+
554
+        $this->em->persist($file);
555
+        $this->em->flush();
556
+    }
557
+
558
+    /**
559
+     * @expectedException Gedmo\Exception\InvalidArgumentException
560
+     * @dataProvider invalidFileInfoClassesProvider
561
+     */
562
+    public function test_setDefaultFileInfoClass_throwExceptionIfInvalidClassArePassed($class)
563
+    {
564
+        $this->listener->setDefaultFileInfoClass($class);
565
+    }
566
+
567
+    public function test_setDefaultFileInfoClass_setClassIfClassIsValid()
568
+    {
569
+        $validClass = 'Gedmo\\Uploadable\\FileInfo\\FileInfoArray';
570
+
571
+        $this->listener->setDefaultFileInfoClass($validClass);
572
+
573
+        $this->assertEquals($validClass, $this->listener->getDefaultFileInfoClass());
574
+    }
575
+
576
+    public function test_useGeneratedFilenameWhenAppendingNumbers()
577
+    {
578
+        // We set the default path on the listener
579
+        $this->listener->setDefaultPath($this->destinationTestDir);
580
+
581
+        $file = new FileWithAlphanumericName();
582
+        $fileInfo = $this->generateUploadedFile('file', $this->testFileWithSpaces, $this->testFilenameWithSpaces);
583
+
584
+        $this->listener->addEntityFileInfo($file, $fileInfo);
585
+
586
+        $this->em->persist($file);
587
+        $this->em->flush();
588
+
589
+        $filePath = $file->getPath().'/'.str_replace(' ', '-', $fileInfo['name']);
590
+
591
+        $this->assertPathEquals($filePath, $file->getFilePath());
592
+
593
+        $file = new FileWithAlphanumericName();
594
+
595
+        $this->listener->addEntityFileInfo($file, $fileInfo);
596
+
597
+        $this->em->persist($file);
598
+        $this->em->flush();
599
+
600
+        $filePath = $file->getPath().'/'.str_replace(' ', '-', str_replace('.txt', '-2.txt', $fileInfo['name']));
601
+
602
+        $this->assertPathEquals($filePath, $file->getFilePath());
603
+    }
604
+
605
+    // Data Providers
606
+    public function invalidFileInfoClassesProvider()
607
+    {
608
+        return array(
609
+            array(''),
610
+            array(false),
611
+            array(null),
612
+            array('FakeFileInfo'),
613
+            array(array()),
614
+            array(new \DateTime())
615
+        );
616
+    }
617
+
618
+    public function uploadExceptionsProvider()
619
+    {
620
+        return array(
621
+            array(1, 'Gedmo\Exception\UploadableIniSizeException'),
622
+            array(2, 'Gedmo\Exception\UploadableFormSizeException'),
623
+            array(3, 'Gedmo\Exception\UploadablePartialException'),
624
+            array(4, 'Gedmo\Exception\UploadableNoFileException'),
625
+            array(6, 'Gedmo\Exception\UploadableNoTmpDirException'),
626
+            array(7, 'Gedmo\Exception\UploadableCantWriteException'),
627
+            array(8, 'Gedmo\Exception\UploadableExtensionException'),
628
+            array(999, 'Gedmo\Exception\UploadableUploadException')
629
+        );
630
+    }
631
+
632
+
633
+
634
+
635
+    // Util
636
+
637
+    private function generateUploadedFile($index = 'image', $filePath = false, $filename = false, array $info = array())
638
+    {
639
+        $defaultInfo = array(
640
+            'tmp_name'          => !$filePath ? $this->testFile : $filePath,
641
+            'name'              => !$filename ? $this->testFilename : $filename,
642
+            'size'              => $this->testFileSize,
643
+            'type'              => $this->testFileMimeType,
644
+            'error'             => 0
645
+        );
646
+
647
+        $info = array_merge($defaultInfo, $info);
648
+
649
+        return $info;
650
+    }
651
+
652
+    protected function getUsedEntityFixtures()
653
+    {
654
+        return array(
655
+            self::IMAGE_CLASS,
656
+            self::ARTICLE_CLASS,
657
+            self::FILE_CLASS,
658
+            self::FILE_WITHOUT_PATH_CLASS,
659
+            self::FILE_APPEND_NUMBER_CLASS,
660
+            self::FILE_WITH_ALPHANUMERIC_NAME_CLASS,
661
+            self::FILE_WITH_SHA1_NAME_CLASS,
662
+            self::FILE_WITH_CUSTOM_FILENAME_GENERATOR_CLASS,
663
+            self::FILE_WITH_MAX_SIZE_CLASS,
664
+            self::FILE_WITH_ALLOWED_TYPES_CLASS,
665
+            self::FILE_WITH_DISALLOWED_TYPES_CLASS
666
+        );
667
+    }
668
+
669
+    private function clearFilesAndDirectories()
670
+    {
671
+        if (is_dir($this->destinationTestDir)) {
672
+            $iter = new \DirectoryIterator($this->destinationTestDir);
673
+
674
+            foreach ($iter as $fileInfo) {
675
+                if (!$fileInfo->isDot()) {
676
+                    @unlink($fileInfo->getPathname());
677
+                }
678
+            }
679
+        }
680
+    }
681
+
682
+    protected function assertPathEquals($expected, $path, $message = '')
683
+    {
684
+        $this->assertEquals($expected, $path, $message);
685
+    }
686
+}
687
+
688
+class FakeFileInfo
689
+{
690
+}
691
+
692
+class FakeFilenameGenerator implements \Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorInterface
693
+{
694
+    public static function generate($filename, $extension, $object = null)
695
+    {
696
+        return '123.txt';
697
+    }
698
+}

+ 62 - 0
vendor/gedmo/doctrine-extensions/tests/bootstrap.php 查看文件

@@ -0,0 +1,62 @@
1
+<?php
2
+
3
+use Doctrine\Common\Annotations\AnnotationRegistry;
4
+use Doctrine\Common\Annotations\AnnotationReader;
5
+use Doctrine\Common\Annotations\CachedReader;
6
+use Doctrine\Common\Cache\ArrayCache;
7
+use Composer\Autoload\ClassLoader;
8
+
9
+/**
10
+ * This is bootstrap for phpUnit unit tests,
11
+ * use README.md for more details
12
+ *
13
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
14
+ * @author Christoph Krämer <cevou@gmx.de>
15
+ * @package Gedmo.Tests
16
+ * @link http://www.gediminasm.org
17
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
18
+ */
19
+
20
+define('TESTS_PATH', __DIR__);
21
+define('TESTS_TEMP_DIR', __DIR__ . '/temp');
22
+define('VENDOR_PATH', realpath(__DIR__ . '/../vendor'));
23
+
24
+if (!class_exists('PHPUnit_Framework_TestCase') ||
25
+    version_compare(PHPUnit_Runner_Version::id(), '3.5') < 0
26
+) {
27
+    die('PHPUnit framework is required, at least 3.5 version');
28
+}
29
+
30
+if (!class_exists('PHPUnit_Framework_MockObject_MockBuilder')) {
31
+    die('PHPUnit MockObject plugin is required, at least 1.0.8 version');
32
+}
33
+
34
+/** @var $loader ClassLoader */
35
+$loader = require __DIR__ . '/../vendor/autoload.php';
36
+
37
+$loader->add('Gedmo\\Mapping\\Mock', __DIR__);
38
+$loader->add('Tool', __DIR__ . '/Gedmo');
39
+// fixture namespaces
40
+$loader->add('Translator\\Fixture', __DIR__ . '/Gedmo');
41
+$loader->add('Translatable\\Fixture', __DIR__ . '/Gedmo');
42
+$loader->add('Timestampable\\Fixture', __DIR__ . '/Gedmo');
43
+$loader->add('Blameable\\Fixture', __DIR__.'/Gedmo');
44
+$loader->add('Tree\\Fixture', __DIR__ . '/Gedmo');
45
+$loader->add('Sluggable\\Fixture', __DIR__ . '/Gedmo');
46
+$loader->add('Sortable\\Fixture', __DIR__ . '/Gedmo');
47
+$loader->add('Mapping\\Fixture', __DIR__ . '/Gedmo');
48
+$loader->add('Loggable\\Fixture', __DIR__ . '/Gedmo');
49
+$loader->add('SoftDeleteable\\Fixture', __DIR__ . '/Gedmo');
50
+$loader->add('Uploadable\\Fixture', __DIR__ . '/Gedmo');
51
+$loader->add('Wrapper\\Fixture', __DIR__ . '/Gedmo');
52
+$loader->add('ReferenceIntegrity\\Fixture', __DIR__ . '/Gedmo');
53
+$loader->add('References\\Fixture', __DIR__ . '/Gedmo');
54
+// stubs
55
+$loader->add('Gedmo\\Uploadable\\Stub', __DIR__);
56
+
57
+AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
58
+Gedmo\DoctrineExtensions::registerAnnotations();
59
+
60
+$reader = new AnnotationReader();
61
+$reader = new CachedReader($reader, new ArrayCache());
62
+$_ENV['annotation_reader'] = $reader;

+ 66 - 0
vendor/gedmo/doctrine-extensions/tests/phpunit.xml.dist 查看文件

@@ -0,0 +1,66 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<phpunit backupGlobals="false"
3
+         backupStaticAttributes="false"
4
+         colors="true"
5
+         convertErrorsToExceptions="true"
6
+         convertNoticesToExceptions="true"
7
+         convertWarningsToExceptions="true"
8
+         processIsolation="false"
9
+         stopOnFailure="false"
10
+         syntaxCheck="false"
11
+         bootstrap="./bootstrap.php"
12
+>
13
+    <testsuites>
14
+        <testsuite name="Translatable Extension">
15
+            <directory suffix="Test.php">./Gedmo/Translatable/</directory>
16
+        </testsuite>
17
+        <testsuite name="Sluggable Extension">
18
+            <directory suffix="Test.php">./Gedmo/Sluggable/</directory>
19
+        </testsuite>
20
+        <testsuite name="Sortable Extension">
21
+            <directory suffix="Test.php">./Gedmo/Sortable/</directory>
22
+        </testsuite>
23
+        <testsuite name="Tree Extension">
24
+            <directory suffix="Test.php">./Gedmo/Tree/</directory>
25
+        </testsuite>
26
+        <testsuite name="Timestampable Extension">
27
+            <directory suffix="Test.php">./Gedmo/Timestampable/</directory>
28
+        </testsuite>
29
+        <testsuite name="Blameable Extension">
30
+            <directory suffix="Test.php">./Gedmo/Blameable/</directory>
31
+        </testsuite>
32
+        <testsuite name="Mapping Extension">
33
+            <directory suffix="Test.php">./Gedmo/Mapping/</directory>
34
+        </testsuite>
35
+        <testsuite name="Loggable Extension">
36
+            <directory suffix="Test.php">./Gedmo/Loggable/</directory>
37
+        </testsuite>
38
+        <testsuite name="Sortable Extension">
39
+            <directory suffix="Test.php">./Gedmo/Sortable/</directory>
40
+        </testsuite>
41
+        <testsuite name="Object wrappers">
42
+            <directory suffix="Test.php">./Gedmo/Wrapper/</directory>
43
+        </testsuite>
44
+        <testsuite name="Translator extension">
45
+            <directory suffix="Test.php">./Gedmo/Translator/</directory>
46
+        </testsuite>
47
+        <testsuite name="SoftDeleteable Extension">
48
+            <directory suffix="Test.php">./Gedmo/SoftDeleteable/</directory>
49
+        </testsuite>
50
+        <testsuite name="Uploadable Extension">
51
+            <directory suffix="Test.php">./Gedmo/Uploadable/</directory>
52
+        </testsuite>
53
+        <testsuite name="ReferenceIntegrity Extension">
54
+            <directory suffix="Test.php">./Gedmo/ReferenceIntegrity/</directory>
55
+        </testsuite>
56
+        <testsuite name="References Extension">
57
+            <directory suffix="Test.php">./Gedmo/References/</directory>
58
+        </testsuite>
59
+    </testsuites>
60
+
61
+    <filter>
62
+        <whitelist>
63
+            <directory>../lib</directory>
64
+        </whitelist>
65
+    </filter>
66
+</phpunit>

+ 87 - 0
vendor/jdorn/sql-formatter/README.md 查看文件

@@ -0,0 +1,87 @@
1
+SqlFormatter
2
+=============
3
+
4
+A lightweight php class for formatting sql statements.  Handles automatic
5
+indentation and syntax highlighting.
6
+
7
+History
8
+============
9
+
10
+I found myself having to debug auto-generated SQL statements all the time and
11
+wanted some way to easily output formatted HTML without having to include a 
12
+huge library or copy and paste into online formatters.
13
+
14
+I was originally planning to extract the formatting code from PhpMyAdmin,
15
+but that was 10,000+ lines of code and used global variables.
16
+
17
+I saw that other people had the same problem and used Stack Overflow user 
18
+losif's answer as a starting point.  http://stackoverflow.com/a/3924147
19
+
20
+Usage
21
+============
22
+
23
+The SqlFormatter class has a static method 'format' which takes a SQL string  
24
+as input and returns a formatted HTML block inside a pre tag. 
25
+
26
+Sample usage:
27
+
28
+```php
29
+<?php
30
+require_once('SqlFormatter.php');
31
+
32
+echo SqlFormatter::format("SELECT * FROM MyTable LIMIT 10");
33
+```
34
+
35
+Sample output:
36
+
37
+![](http://github.com/jdorn/sql-formatter/raw/master/examples/SqlFormatterExample.png)
38
+
39
+Syntax Highlighting Only
40
+-------------------------
41
+
42
+There is also a static method 'highlight' that only does syntax highlighting 
43
+and preserves all original whitespace.
44
+
45
+This is useful for sql that is already well formatted and just needs to be a little
46
+easier to read.
47
+
48
+```php
49
+<?php
50
+echo SqlFormatter::highlight("SELECT * FROM MyTable LIMIT 10");
51
+```
52
+
53
+Split SQL String into Queries
54
+--------------------------
55
+
56
+Another feature, which is unrelated to formatting, is the ability to break up a SQL string into multiple queries.  
57
+
58
+For Example:
59
+
60
+```sql
61
+DROP TABLE IF EXISTS MyTable;
62
+CREATE TABLE MyTable ( id int );
63
+INSERT INTO MyTable	(id)
64
+	VALUES
65
+	(1),(2),(3),(4);
66
+SELECT * FROM MyTable;
67
+```
68
+
69
+```php
70
+$queries = SqlFormatter::splitQuery($sql);
71
+```
72
+$queries is now an array of the 4 queries without trailing semicolons.
73
+
74
+Why not just use explode(';',$sql) or regular expressions?  The following example sql and others like it
75
+are impossible to split correctly using regular expressions.
76
+
77
+```sql
78
+SELECT ";"; SELECT ";\"; a;";
79
+SELECT ";
80
+    abc";
81
+SELECT a,b #comment;
82
+FROM test;
83
+```
84
+
85
+The splitQuery method will still fail in the following cases:
86
+*    The DELIMITER command can be used to change the delimiter from the default ';' to something else.  
87
+*    The CREATE PROCEDURE command

+ 0 - 0
vendor/jdorn/sql-formatter/examples/SqlFormatterExample.png 查看文件


部分文件因文件數量過多而無法顯示