Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Sharding / SQLAzure / Schema / MultiTenantVisitor.php
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\Sharding\SQLAzure\Schema;
21
22 use Doctrine\DBAL\Schema\Visitor\Visitor,
23     Doctrine\DBAL\Schema\Table,
24     Doctrine\DBAL\Schema\Schema,
25     Doctrine\DBAL\Schema\Column,
26     Doctrine\DBAL\Schema\ForeignKeyConstraint,
27     Doctrine\DBAL\Schema\Constraint,
28     Doctrine\DBAL\Schema\Sequence,
29     Doctrine\DBAL\Schema\Index;
30
31 /**
32  * Converts a single tenant schema into a multi-tenant schema for SQL Azure
33  * Federations under the following assumptions:
34  *
35  * - Every table is part of the multi-tenant application, only explicitly
36  *   excluded tables are non-federated. The behavior of the tables being in
37  *   global or federated database is undefined. It depends on you selecting a
38  *   federation before DDL statements or not.
39  * - Every Primary key of a federated table is extended by another column
40  *   'tenant_id' with a default value of the SQLAzure function
41  *   `federation_filtering_value('tenant_id')`.
42  * - You always have to work with `filtering=On` when using federations with this
43  *   multi-tenant approach.
44  * - Primary keys are either using globally unique ids (GUID, Table Generator)
45  *   or you explicitly add the tenent_id in every UPDATE or DELETE statement
46  *   (otherwise they will affect the same-id rows from other tenents as well).
47  *   SQLAzure throws errors when you try to create IDENTIY columns on federated
48  *   tables.
49  *
50  * @author Benjamin Eberlei <kontakt@beberlei.de>
51  */
52 class MultiTenantVisitor implements Visitor
53 {
54     /**
55      * @var array
56      */
57     private $excludedTables = array();
58
59     /**
60      * @var string
61      */
62     private $tenantColumnName;
63
64     /**
65      * @var string
66      */
67     private $tenantColumnType = 'integer';
68
69     /**
70      * Name of the federation distribution, defaulting to the tenantColumnName
71      * if not specified.
72      *
73      * @var string
74      */
75     private $distributionName;
76
77     public function __construct(array $excludedTables = array(), $tenantColumnName = 'tenant_id', $distributionName = null)
78     {
79         $this->excludedTables = $excludedTables;
80         $this->tenantColumnName = $tenantColumnName;
81         $this->distributionName = $distributionName ?: $tenantColumnName;
82     }
83
84     /**
85      * @param Table $table
86      */
87     public function acceptTable(Table $table)
88     {
89         if (in_array($table->getName(), $this->excludedTables)) {
90             return;
91         }
92
93         $table->addColumn($this->tenantColumnName, $this->tenantColumnType, array(
94             'default' => "federation_filtering_value('". $this->distributionName ."')",
95         ));
96
97         $clusteredIndex = $this->getClusteredIndex($table);
98
99         $indexColumns = $clusteredIndex->getColumns();
100         $indexColumns[] = $this->tenantColumnName;
101
102         if ($clusteredIndex->isPrimary()) {
103             $table->dropPrimaryKey();
104             $table->setPrimaryKey($indexColumns);
105         } else {
106             $table->dropIndex($clusteredIndex->getName());
107             $table->addIndex($indexColumns, $clusteredIndex->getName());
108             $table->getIndex($clusteredIndex->getName())->addFlag('clustered');
109         }
110     }
111
112     private function getClusteredIndex($table)
113     {
114         foreach ($table->getIndexes() as $index) {
115             if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) {
116                 return $index;
117             } else if ($index->hasFlag('clustered')) {
118                 return $index;
119             }
120         }
121         throw new \RuntimeException("No clustered index found on table " . $table->getName());
122     }
123
124     /**
125      * @param Schema $schema
126      */
127     public function acceptSchema(Schema $schema)
128     {
129     }
130
131     /**
132      * @param Column $column
133      */
134     public function acceptColumn(Table $table, Column $column)
135     {
136     }
137
138     /**
139      * @param Table $localTable
140      * @param ForeignKeyConstraint $fkConstraint
141      */
142     public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
143     {
144     }
145
146     /**
147      * @param Table $table
148      * @param Index $index
149      */
150     public function acceptIndex(Table $table, Index $index)
151     {
152     }
153
154     /**
155      * @param Sequence $sequence
156      */
157     public function acceptSequence(Sequence $sequence)
158     {
159     }
160 }
161