List of paths which really exist
115 | */
116 | protected function paths( string $relpath = '' ) : array
117 | {
118 | $list = [];
119 | $relpath = DIRECTORY_SEPARATOR . trim( $relpath, DIRECTORY_SEPARATOR );
120 |
121 | foreach( $this->up->paths() as $path )
122 | {
123 | $abspath = $path . $relpath;
124 |
125 | if( file_exists( $abspath ) ) {
126 | $list[] = $abspath;
127 | }
128 | }
129 |
130 | return $list;
131 | }
132 | }
--------------------------------------------------------------------------------
/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Upscheme - Database migrations and schema updates made easy
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
53 |
54 |
55 |
60 |
61 | {{ content }}
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/tests/Upscheme/Schema/SequenceTest.php:
--------------------------------------------------------------------------------
1 | dbmock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
21 | ->disableOriginalConstructor()
22 | ->getMock();
23 |
24 | $this->seqmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Sequence' )
25 | ->disableOriginalConstructor()
26 | ->getMock();
27 |
28 | $this->object = new \Aimeos\Upscheme\Schema\Sequence( $this->dbmock, $this->seqmock );
29 | }
30 |
31 |
32 | protected function tearDown() : void
33 | {
34 | unset( $this->object, $this->seqmock, $this->dbmock );
35 | }
36 |
37 |
38 | public function testCall()
39 | {
40 | $this->seqmock->expects( $this->once() )->method( 'getAllocationSize' );
41 |
42 | $this->object->getAllocationSize();
43 | }
44 |
45 |
46 | public function testCallMacro()
47 | {
48 | \Aimeos\Upscheme\Schema\Sequence::macro( 'unittest', function() { return 'yes'; } );
49 |
50 | $this->assertEquals( 'yes', $this->object->unittest() );
51 | }
52 |
53 |
54 | public function testGetMagic()
55 | {
56 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Sequence' )
57 | ->disableOriginalConstructor()
58 | ->onlyMethods( ['cache'] )
59 | ->getMock();
60 |
61 | $object->expects( $this->once() )->method( 'cache' )
62 | ->willReturn( 1000 );
63 |
64 | $this->assertEquals( 1000, $object->cache );
65 | }
66 |
67 |
68 | public function testSetMagic()
69 | {
70 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Sequence' )
71 | ->disableOriginalConstructor()
72 | ->onlyMethods( ['cache'] )
73 | ->getMock();
74 |
75 | $object->expects( $this->once() )->method( 'cache' );
76 |
77 | $object->cache = 100;
78 | }
79 |
80 |
81 | public function testName()
82 | {
83 | $this->seqmock->expects( $this->once() )->method( 'getName' )->willReturn( 'seq_name' );
84 |
85 | $this->assertEquals( 'seq_name', $this->object->name() );
86 | }
87 |
88 |
89 | public function testCacheGet()
90 | {
91 | $this->assertEquals( false, $this->object->cache() );
92 | }
93 |
94 |
95 | public function testCacheSet()
96 | {
97 | $this->seqmock->expects( $this->once() )->method( 'setCache' );
98 |
99 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->cache( true ) );
100 | }
101 |
102 |
103 | public function testStartGet()
104 | {
105 | $this->seqmock->expects( $this->once() )->method( 'getInitialValue' )->willReturn( 1 );
106 |
107 | $this->assertEquals( 1, $this->object->start() );
108 | }
109 |
110 |
111 | public function testStartSet()
112 | {
113 | $this->seqmock->expects( $this->once() )->method( 'setInitialValue' );
114 |
115 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->start( 10 ) );
116 | }
117 |
118 |
119 | public function testStepGet()
120 | {
121 | $this->seqmock->expects( $this->once() )->method( 'getAllocationSize' )->willReturn( 1 );
122 |
123 | $this->assertEquals( 1, $this->object->step() );
124 | }
125 |
126 |
127 | public function testStepSet()
128 | {
129 | $this->seqmock->expects( $this->once() )->method( 'setAllocationSize' );
130 |
131 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->step( 2 ) );
132 | }
133 |
134 |
135 | public function testUp()
136 | {
137 | $this->dbmock->expects( $this->once() )->method( 'up' );
138 |
139 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->up() );
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/tests/Upscheme/Schema/ForeignTest.php:
--------------------------------------------------------------------------------
1 | dbmock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
22 | ->disableOriginalConstructor()
23 | ->getMock();
24 |
25 | $this->tablemock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Table' )
26 | ->disableOriginalConstructor()
27 | ->getMock();
28 |
29 | $this->dbalmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
30 | ->disableOriginalConstructor()
31 | ->getMock();
32 |
33 | $this->object = new \Aimeos\Upscheme\Schema\Foreign(
34 | $this->dbmock, $this->tablemock, $this->dbalmock,
35 | ['local'], 'fktable', ['foreign'], 'fk_name'
36 | );
37 | }
38 |
39 |
40 | protected function tearDown() : void
41 | {
42 | unset( $this->object, $this->dbalmock, $this->tablemock );
43 | }
44 |
45 |
46 | public function testCallMacro()
47 | {
48 | \Aimeos\Upscheme\Schema\Foreign::macro( 'unittest', function() { return 'yes'; } );
49 |
50 | $this->assertEquals( 'yes', $this->object->unittest() );
51 | }
52 |
53 |
54 | public function testCall()
55 | {
56 | $this->expectException( '\BadMethodCallException' );
57 | $this->object->unittest2();
58 | }
59 |
60 |
61 | public function testGetMagic()
62 | {
63 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Foreign' )
64 | ->disableOriginalConstructor()
65 | ->onlyMethods( ['onDelete'] )
66 | ->getMock();
67 |
68 | $object->expects( $this->once() )->method( 'onDelete' )
69 | ->willReturn( 'CASCADE' );
70 |
71 | $this->assertEquals( 'CASCADE', $object->onDelete );
72 | }
73 |
74 |
75 | public function testSetMagic()
76 | {
77 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Foreign' )
78 | ->disableOriginalConstructor()
79 | ->onlyMethods( ['onDelete'] )
80 | ->getMock();
81 |
82 | $object->expects( $this->once() )->method( 'onDelete' );
83 |
84 | $object->onDelete = 'RESTRICT';
85 | }
86 |
87 |
88 | public function testDo()
89 | {
90 | $this->dbalmock->expects( $this->once() )->method( 'removeForeignKey' );
91 | $this->dbalmock->expects( $this->once() )->method( 'addForeignKeyConstraint' );
92 |
93 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Foreign::class, $this->object->do( 'SET NULL' ) );
94 | $this->assertEquals( 'SET NULL', $this->object->onDelete );
95 | $this->assertEquals( 'SET NULL', $this->object->onUpdate );
96 | }
97 |
98 |
99 | public function testName()
100 | {
101 | $this->assertEquals( 'fk_name', $this->object->name() );
102 | }
103 |
104 |
105 | public function testOnDeleteGet()
106 | {
107 | $this->assertEquals( 'CASCADE', $this->object->onDelete() );
108 | }
109 |
110 |
111 | public function testOnDeleteSet()
112 | {
113 | $this->dbalmock->expects( $this->once() )->method( 'removeForeignKey' );
114 | $this->dbalmock->expects( $this->once() )->method( 'addForeignKeyConstraint' );
115 |
116 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Foreign::class, $this->object->onDelete( 'RESTRICT' ) );
117 | }
118 |
119 |
120 | public function testOnUpdateGet()
121 | {
122 | $this->assertEquals( 'CASCADE', $this->object->onUpdate() );
123 | }
124 |
125 |
126 | public function testOnUpdateSet()
127 | {
128 | $this->dbalmock->expects( $this->once() )->method( 'removeForeignKey' );
129 | $this->dbalmock->expects( $this->once() )->method( 'addForeignKeyConstraint' );
130 |
131 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Foreign::class, $this->object->onUpdate( 'RESTRICT' ) );
132 | }
133 |
134 |
135 | public function testUp()
136 | {
137 | $this->tablemock->expects( $this->once() )->method( 'up' );
138 |
139 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Foreign::class, $this->object->up() );
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Schema/Sequence.php:
--------------------------------------------------------------------------------
1 | db = $db;
43 | $this->sequence = $sequence;
44 | }
45 |
46 |
47 | /**
48 | * Calls custom methods or passes unknown method calls to the Doctrine table object
49 | *
50 | * @param string $method Name of the method
51 | * @param array $args Method parameters
52 | * @return mixed Return value of the called method
53 | */
54 | public function __call( string $method, array $args )
55 | {
56 | if( self::macro( $method ) ) {
57 | return $this->call( $method, ...$args );
58 | }
59 |
60 | return $this->sequence->{$method}( ...$args );
61 | }
62 |
63 |
64 | /**
65 | * Returns the value for the given sequence option
66 | *
67 | * @param string $name Sequence option name
68 | * @return mixed Sequence option value
69 | */
70 | public function __get( string $name )
71 | {
72 | return $this->{$name}();
73 | }
74 |
75 |
76 | /**
77 | * Sets the new value for the given sequence option
78 | *
79 | * @param string $name Sequence option name
80 | * @param mixed $value Sequence option value
81 | */
82 | public function __set( string $name, $value )
83 | {
84 | $this->{$name}( $value );
85 | }
86 |
87 |
88 | /**
89 | * Sets the cached size of the sequence or returns the current value
90 | *
91 | * @param int $value New number of sequence IDs cached by the client or NULL to return current value
92 | * @return self|int Same object for setting value, current value without parameter
93 | */
94 | public function cache( ?int $value = null )
95 | {
96 | if( $value === null ) {
97 | return $this->sequence->getCache();
98 | }
99 |
100 | $this->sequence->setCache( $value );
101 | return $this;
102 | }
103 |
104 |
105 | /**
106 | * Returns the name of the sequence
107 | *
108 | * @return string Sequence name
109 | */
110 | public function name()
111 | {
112 | return $this->sequence->getName();
113 | }
114 |
115 |
116 | /**
117 | * Sets the new start value of the sequence or returns the current value
118 | *
119 | * @param int $value New start value of the sequence or NULL to return current value
120 | * @return self|int Same object for setting value, current value without parameter
121 | */
122 | public function start( ?int $value = null )
123 | {
124 | if( $value === null ) {
125 | return $this->sequence->getInitialValue();
126 | }
127 |
128 | $this->sequence->setInitialValue( $value );
129 | return $this;
130 | }
131 |
132 |
133 | /**
134 | * Sets the step size of new sequence values or returns the current value
135 | *
136 | * @param int $value New step size the sequence is incremented or decremented by or NULL to return current value
137 | * @return self|int Same object for setting value, current value without parameter
138 | */
139 | public function step( ?int $value = null )
140 | {
141 | if( $value === null ) {
142 | return $this->sequence->getAllocationSize();
143 | }
144 |
145 | $this->sequence->setAllocationSize( $value );
146 | return $this;
147 | }
148 |
149 |
150 | /**
151 | * Applies the changes to the database schema
152 | *
153 | * @return self Same object for fluid method calls
154 | */
155 | public function up() : self
156 | {
157 | $this->db->up();
158 | return $this;
159 | }
160 | }
--------------------------------------------------------------------------------
/assets/css/style.scss:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @import "jekyll-theme-cayman";
5 |
6 |
7 | body {
8 | background-color: #333;
9 | line-height: 1.75;
10 | }
11 |
12 | a {
13 | display: inline-block;
14 | line-height: 2.5;
15 | }
16 |
17 | header.page-header {
18 | background-color: #25a2db;
19 | background-image: linear-gradient(120deg, #16629c, #25a2db);
20 | }
21 |
22 | header h1>a {
23 | color: #fff;
24 | }
25 |
26 | header .icon.github {
27 | display: none;
28 | }
29 |
30 | header .octocat {
31 | width: 3rem;
32 | }
33 |
34 | header svg {
35 | display: none;
36 | padding: 0.5rem;
37 | color: #fff;
38 | }
39 |
40 | .container {
41 | background-color: #fff;
42 | display: flex;
43 | margin: auto;
44 | }
45 |
46 | .sidebar {
47 | position: -webkit-sticky;
48 | position: sticky;
49 | top: 0;
50 | order: 1;
51 | padding: 1rem;
52 | height: 100vh;
53 | overflow-y: auto;
54 | background-color: #f3f6fa;
55 | scrollbar-color: #606c71 #F5F5F5;
56 | scrollbar-width: 5px;
57 | }
58 |
59 | .sidebar::-webkit-scrollbar-thumb {
60 | background-color: #606c71;
61 | }
62 |
63 | .sidebar::-webkit-scrollbar-track {
64 | background-color: #F5F5F5;
65 | }
66 |
67 | .sidebar::-webkit-scrollbar {
68 | width: 5px;
69 | }
70 |
71 | .sidebar .search {
72 | width: calc(100% + 1rem);
73 | margin: 0 -0.5rem;
74 | margin-bottom: 1rem;
75 | padding: 0.25rem;
76 | color: #606c71;
77 | }
78 |
79 | .sidebar .search:focus {
80 | outline: none;
81 | }
82 |
83 | .sidebar .search::placeholder {
84 | color: #aaa;
85 | }
86 |
87 | .sidebar a {
88 | display: block;
89 | }
90 |
91 | .sidebar li.hide {
92 | display: none;
93 | }
94 |
95 | .main-content {
96 | margin: 0!important;
97 | }
98 |
99 | .main-content .badge {
100 | display: inline-block;
101 | padding: 0.45rem 0;
102 | }
103 |
104 | .main-content h1, .main-content h2, .main-content h3,
105 | .main-content h4, .main-content h5, .main-content h6 {
106 | padding-top: 3rem;
107 | margin-top: -1rem;
108 | }
109 |
110 | .main-content nav {
111 | column-count: 3;
112 | column-width: 8rem;
113 | }
114 |
115 | .main-content nav a {
116 | display: block;
117 | padding: 0 1rem;
118 | }
119 |
120 | .main-content .highlight pre, .main-content pre {
121 | line-height: 1.75;
122 | }
123 |
124 | .method-list {
125 | margin-top: 0;
126 | }
127 |
128 | @media screen and (min-width: 64em) {
129 | .page-header {
130 | padding: 2.5rem 6rem;
131 | }
132 | header .btn {
133 | padding: 0.25rem 1rem;
134 | }
135 | header .logo {
136 | max-height: 7.5rem;
137 | }
138 | .container {
139 | max-width: 80rem;
140 | margin: 0 auto;
141 | }
142 | .sidebar {
143 | width: 16rem;
144 | }
145 | .main-content {
146 | width: calc(100% - 16rem);
147 | padding: 4rem!important;
148 | }
149 | }
150 |
151 | @media screen and (max-width: 64em) {
152 | .page-header {
153 | padding: 1.5rem 4rem;
154 | }
155 | header .btn {
156 | padding: 0.125rem 0.9rem;
157 | }
158 | header .logo {
159 | max-height: 6rem;
160 | }
161 | .sidebar {
162 | width: 12rem;
163 | }
164 | .main-content {
165 | width: calc(100% - 12rem);
166 | padding: 2rem!important;
167 | }
168 | }
169 |
170 | @media screen and (max-width: 48em) {
171 | .page-header {
172 | position: fixed;
173 | padding: 0.5rem;
174 | height: 4rem;
175 | width: 100%;
176 | display: flex;
177 | justify-content: space-between;
178 | }
179 | header .logo {
180 | height: 100%;
181 | }
182 | .project-name {
183 | font-size: 1.75rem!important;
184 | }
185 | .project-tagline {
186 | display: none;
187 | }
188 | header .icon.github {
189 | display: inline-block;
190 | }
191 | header .btn.github {
192 | display: none;
193 | }
194 | header .open.show,
195 | header .close.show {
196 | display: inline-block;
197 | }
198 | .sidebar {
199 | position: fixed;
200 | right: -16rem;
201 | top: 4rem;
202 | width: 16rem;
203 | padding: 1rem 2rem;
204 | height: calc(100vh - 4rem);
205 | transition: right .5s;
206 | scrollbar-color: #f0f0f0 #606c71;
207 | background-color: #333;
208 | color: #f0f0f0;
209 | }
210 | .sidebar::-webkit-scrollbar-thumb {
211 | background-color: #f0f0f0;
212 | }
213 | .sidebar::-webkit-scrollbar-track {
214 | background-color: #606c71;
215 | }
216 | .sidebar.show {
217 | right: 0;
218 | }
219 | .sidebar a {
220 | color: #f0f0f0;
221 | }
222 | .main-content {
223 | margin-top: 4rem!important;
224 | width: 100%;
225 | }
226 | }
227 |
228 | /* Better contrast of code examples */
229 |
230 | .highlight .bp, .highlight .c1, .highlight .cd, .highlight .cm, .highlight .cp, .highlight .cs, .highlight .gh { color: #707070 }
231 | .highlight .nb { color: #007aa0 }
232 | .highlight .nv { color: #007b7b }
233 | .highlight .no, .highlight .nv { color: #007b7b }
234 | .highlight .mi, .highlight .mf { color: #007e7e }
235 | .highlight .err { color: inherit; background-color: inherit }
236 |
--------------------------------------------------------------------------------
/assets/upscheme-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
52 |
--------------------------------------------------------------------------------
/tests/Upscheme/UpTest.php:
--------------------------------------------------------------------------------
1 | object = $this->getMockBuilder( '\Aimeos\Upscheme\Up' )
19 | ->setConstructorArgs( [['driver' => 'sqlite'], 'test'] )
20 | ->onlyMethods( [] )
21 | ->getMock();
22 | }
23 |
24 |
25 | protected function tearDown() : void
26 | {
27 | unset( $this->object );
28 | }
29 |
30 |
31 | public function testConstruct()
32 | {
33 | $object = new \Aimeos\Upscheme\Up( ['driver' => ''], 'test' );
34 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $object );
35 | }
36 |
37 |
38 | public function testConstructConfig()
39 | {
40 | $this->expectException( '\RuntimeException' );
41 | new \Aimeos\Upscheme\Up( [], 'test' );
42 | }
43 |
44 |
45 | public function testConstructPath()
46 | {
47 | $this->expectException( '\RuntimeException' );
48 | new \Aimeos\Upscheme\Up( ['driver' => ''], [] );
49 | }
50 |
51 |
52 | public function testAutoload()
53 | {
54 | $object = new \Aimeos\Upscheme\Up( ['driver' => 'pdo_sqlite'], dirname( __DIR__ ) . '/Tasks/test' );
55 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $object->up() );
56 | }
57 |
58 |
59 | public function testAutoloadCustom()
60 | {
61 | \Aimeos\Upscheme\Up::macro( 'autoload', function( $class ) { return true; } );
62 |
63 | $object = new \Aimeos\Upscheme\Up( ['driver' => 'pdo_sqlite'], dirname( __DIR__ ) . '/Tasks/test' );
64 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $object->up() );
65 | }
66 |
67 |
68 | public function testDb()
69 | {
70 | $object = new \Aimeos\Upscheme\Up( ['test' => ['driver' => 'pdo_sqlite', 'path' => 'up.test']], 'testpath' );
71 |
72 | $db = $object->db( 'test' );
73 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $db );
74 |
75 | $db2 = $object->db( 'test' );
76 | $this->assertSame( $db, $db2 );
77 | }
78 |
79 |
80 | public function testDbNew()
81 | {
82 | $object = new \Aimeos\Upscheme\Up( ['test' => ['driver' => 'pdo_sqlite', 'path' => 'up.test']], 'testpath' );
83 |
84 | $db = $object->db( 'test' );
85 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $db );
86 |
87 | $db2 = $object->db( 'test', true );
88 | $this->assertNotSame( $db, $db2 );
89 | }
90 |
91 |
92 | public function testDbFallback()
93 | {
94 | $object = new \Aimeos\Upscheme\Up( ['test' => ['driver' => 'pdo_sqlite', 'path' => 'up.test']], 'testpath' );
95 |
96 | $db = $object->db( 'test2' );
97 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $db );
98 |
99 | $db2 = $object->db();
100 | $this->assertSame( $db, $db2 );
101 | }
102 |
103 |
104 | public function testDbSingle()
105 | {
106 | $object = new \Aimeos\Upscheme\Up( ['driver' => 'pdo_sqlite', 'path' => 'up.test'], 'testpath' );
107 |
108 | $db = $object->db( 'test2' );
109 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $db );
110 |
111 | $db2 = $object->db();
112 | $this->assertSame( $db, $db2 );
113 | }
114 |
115 |
116 | public function testDbCustom()
117 | {
118 | \Aimeos\Upscheme\Up::macro( 'connect', function( array $cfg ) {
119 | return \Doctrine\DBAL\DriverManager::getConnection( ['driver' => 'pdo_sqlite', 'path' => 'up.test'] );
120 | } );
121 |
122 | $result = ( new \Aimeos\Upscheme\Up( ['driver' => 'pdo_mysql'], 'testpath' ) )->db();
123 | \Aimeos\Upscheme\Up::unmacro( 'connect' );
124 |
125 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $result );
126 | }
127 |
128 |
129 | public function testInfo()
130 | {
131 | $this->expectOutputString( 'test' . PHP_EOL );
132 | $this->object->info( 'test', '' );
133 | }
134 |
135 |
136 | public function testInfoVerbose()
137 | {
138 | $this->expectOutputString( '' );
139 | $this->object->info( 'test', 'v' );
140 | }
141 |
142 |
143 | public function testInfoCustom()
144 | {
145 | \Aimeos\Upscheme\Up::macro( 'info', function( $msg ) { echo 'custom'; } );
146 |
147 | $this->expectOutputString( 'custom' );
148 | $this->object->info( 'test', 'v' );
149 |
150 | \Aimeos\Upscheme\Up::unmacro( 'info' );
151 | }
152 |
153 |
154 | public function testPaths()
155 | {
156 | $this->assertEquals( ['test'], $this->object->paths() );
157 | }
158 |
159 |
160 | public function testUse()
161 | {
162 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, \Aimeos\Upscheme\Up::use( ['driver' => ''], 'test' ) );
163 | }
164 |
165 |
166 | public function testVerbose()
167 | {
168 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $this->object->verbose() );
169 | }
170 |
171 |
172 | public function testVerboseMore()
173 | {
174 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $this->object->verbose( 'vv' ) );
175 | }
176 |
177 |
178 | public function testVerboseCustom()
179 | {
180 | \Aimeos\Upscheme\Up::macro( 'verbose', function( $level ) { return 3; } );
181 | $result = $this->object->verbose( 'v' );
182 | \Aimeos\Upscheme\Up::unmacro( 'info' );
183 |
184 | $this->assertInstanceOf( \Aimeos\Upscheme\Up::class, $result );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/src/Schema/Foreign.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | private $localcol;
44 |
45 | /**
46 | * @var string
47 | */
48 | private $fktable;
49 |
50 | /**
51 | * @var array
52 | */
53 | private $fkcol;
54 |
55 | /**
56 | * @var string|null
57 | */
58 | private $name;
59 |
60 | /**
61 | * @var array
62 | */
63 | private $opts;
64 |
65 |
66 | /**
67 | * Initializes the foreign key object
68 | *
69 | * @param \Aimeos\Upscheme\Schema\DB $db DB schema object
70 | * @param \Aimeos\Upscheme\Schema\Table $table Table schema object
71 | * @param \Doctrine\DBAL\Schema\Table $dbaltable Doctrine table object
72 | * @param array $localcol List of columns from the current table spawning the foreign key constraint
73 | * @param string $fktable Name of the referenced table
74 | * @param array $fkcol List of columns from the referenced table spawning the foreign key constraint
75 | * @param string|null $name $name Name of the foreign key constraint and index or NULL for autogenerated name
76 | */
77 | public function __construct( DB $db, Table $table, DbalTable $dbaltable, array $localcol, string $fktable, array $fkcol, ?string $name = null )
78 | {
79 | $this->db = $db;
80 | $this->table = $table;
81 | $this->dbaltable = $dbaltable;
82 | $this->localcol = $localcol;
83 | $this->fktable = $fktable;
84 | $this->fkcol = $fkcol;
85 | $this->name = $name;
86 | $this->opts = [
87 | 'onDelete' => 'CASCADE',
88 | 'onUpdate' => 'CASCADE',
89 | ];
90 |
91 | if( !$table->hasIndex( (string) $name ) ) {
92 | $table->index( $localcol, $name );
93 | }
94 |
95 | if( !$table->hasForeign( (string) $name ) )
96 | {
97 | $lcol = $fcol = [];
98 |
99 | foreach( (array) $localcol as $key => $column ) {
100 | $lcol[$key] = $db->qi( $column );
101 | }
102 |
103 | foreach( (array) $fkcol as $key => $column ) {
104 | $fcol[$key] = $db->qi( $column );
105 | }
106 |
107 | $dbaltable->addForeignKeyConstraint( $db->qi( $fktable ), $lcol, $fcol, $this->opts, $name ? $db->qi( $name ) : null );
108 | }
109 | }
110 |
111 |
112 | /**
113 | * Calls custom methods or passes unknown method calls to the Doctrine table object
114 | *
115 | * @param string $method Name of the method
116 | * @param array $args Method parameters
117 | * @return mixed Return value of the called method
118 | */
119 | public function __call( string $method, array $args )
120 | {
121 | if( self::macro( $method ) ) {
122 | return $this->call( $method, ...$args );
123 | }
124 |
125 | throw new \BadMethodCallException( sprintf( 'Unknown method "%1$s" in %2$s', $method, __CLASS__ ) );
126 | }
127 |
128 |
129 | /**
130 | * Returns the value for the given sequence option
131 | *
132 | * @param string $name Sequence option name
133 | * @return mixed Sequence option value
134 | */
135 | public function __get( string $name )
136 | {
137 | return $this->{$name}();
138 | }
139 |
140 |
141 | /**
142 | * Sets the new value for the given sequence option
143 | *
144 | * @param string $name Sequence option name
145 | * @param mixed $value Sequence option value
146 | */
147 | public function __set( string $name, $value )
148 | {
149 | $this->{$name}( $value );
150 | }
151 |
152 |
153 | /**
154 | * Sets the action if referenced rows are deleted or updated
155 | *
156 | * Available actions are:
157 | * - CASCADE : Delete or update referenced value
158 | * - NO ACTION : No change in referenced value
159 | * - RESTRICT : Forbid changing values
160 | * - SET DEFAULT : Set referenced value to the default value
161 | * - SET NULL : Set referenced value to NULL
162 | *
163 | * @param string $action Performed action
164 | * @return self Same object for fluid method calls
165 | */
166 | public function do( string $action ) : self
167 | {
168 | if( $this->opts['onDelete'] !== $action || $this->opts['onUpdate'] !== $action )
169 | {
170 | $this->opts['onDelete'] = $action;
171 | $this->opts['onUpdate'] = $action;
172 |
173 | return $this->replace();
174 | }
175 |
176 | return $this;
177 | }
178 |
179 |
180 | /**
181 | * Returns the current name of the foreign key constraint
182 | *
183 | * @return string|null Name of the constraint or NULL if no name is available
184 | */
185 | public function name()
186 | {
187 | return $this->name;
188 | }
189 |
190 |
191 | /**
192 | * Sets the action if the referenced row is deleted or returns the current value
193 | *
194 | * Available actions are:
195 | * - CASCADE : Delete referenced value
196 | * - NO ACTION : No change in referenced value
197 | * - RESTRICT : Forbid changing values
198 | * - SET DEFAULT : Set referenced value to the default value
199 | * - SET NULL : Set referenced value to NULL
200 | *
201 | * @param string|null $value Performed action or NULL to return current value
202 | * @return self|string Same object for setting the value, current value without parameter
203 | */
204 | public function onDelete( ?string $value = null )
205 | {
206 | if( $value === null ) {
207 | return $this->opts['onDelete'];
208 | }
209 |
210 | if( $this->opts['onDelete'] !== $value )
211 | {
212 | $this->opts['onDelete'] = $value;
213 | return $this->replace();
214 | }
215 |
216 | return $this;
217 | }
218 |
219 |
220 | /**
221 | * Sets the action if the referenced row is updated or returns the current value
222 | *
223 | * Available actions are:
224 | * - CASCADE : Update referenced value
225 | * - NO ACTION : No change in referenced value
226 | * - RESTRICT : Forbid changing values
227 | * - SET DEFAULT : Set referenced value to the default value
228 | * - SET NULL : Set referenced value to NULL
229 | *
230 | * @param string|null $value Performed action or NULL to return current value
231 | * @return self|string Same object for setting the value, current value without parameter
232 | */
233 | public function onUpdate( ?string $value = null )
234 | {
235 | if( $value === null ) {
236 | return $this->opts['onUpdate'];
237 | }
238 |
239 | if( $this->opts['onUpdate'] !== $value )
240 | {
241 | $this->opts['onUpdate'] = $value;
242 | return $this->replace();
243 | }
244 |
245 | return $this;
246 | }
247 |
248 |
249 | /**
250 | * Applies the changes to the database schema
251 | *
252 | * @return self Same object for fluid method calls
253 | */
254 | public function up() : self
255 | {
256 | $this->table->up();
257 | return $this;
258 | }
259 |
260 |
261 | /**
262 | * Deletes the current constraint and creates a new one
263 | *
264 | * @param string|null $newname Name of the new constraint or same name if NULL
265 | * @return self Same object for fluid method calls
266 | */
267 | protected function replace( ?string $newname = null ) : self
268 | {
269 | $newname = $newname ?: $this->name;
270 |
271 | if( $this->name )
272 | {
273 | $this->dbaltable->removeForeignKey( $this->db->qi( $this->name ) );
274 | $this->table->dropIndex( $this->name );
275 | }
276 |
277 | $lcol = $fcol = [];
278 |
279 | foreach( $this->localcol as $key => $column ) {
280 | $lcol[$key] = $this->db->qi( $column );
281 | }
282 |
283 | foreach( $this->fkcol as $key => $column ) {
284 | $fcol[$key] = $this->db->qi( $column );
285 | }
286 |
287 | $this->table->index( $this->localcol, $newname )->up();
288 | $this->dbaltable->addForeignKeyConstraint(
289 | $this->db->qi( $this->fktable ),
290 | $lcol, $fcol, $this->opts,
291 | $newname ? $this->db->qi( $newname ) : null
292 | );
293 |
294 | $this->name = $newname;
295 | return $this;
296 | }
297 | }
--------------------------------------------------------------------------------
/src/Generate.php:
--------------------------------------------------------------------------------
1 | seqtpl = $this->read( $dir . $ds . 'stubs' . $ds . 'sequence.stub' );
34 | $this->tabletpl = $this->read( $dir . $ds . 'stubs' . $ds . 'table.stub' );
35 | $this->viewtpl = $this->read( $dir . $ds . 'stubs' . $ds . 'view.stub' );
36 |
37 | $this->path = $path;
38 | }
39 |
40 |
41 | /**
42 | * Generates the migration files
43 | *
44 | * @param array $schema Associative list of schema definitions
45 | * @param string $dbname Name of the database
46 | * @throws \RuntimeException If a file can't be created
47 | */
48 | public function __invoke( array $schema, string $dbname = '' ) : void
49 | {
50 | $ds = DIRECTORY_SEPARATOR;
51 | $prefix = $dbname ? preg_replace( '/[^A-Za-z0-9]/', '', $dbname ) . '_' : '';
52 |
53 | $seqtpl = str_replace( '{{DB}}', $dbname, $this->seqtpl );
54 | $tabletpl = str_replace( '{{DB}}', $dbname, $this->tabletpl );
55 | $viewtpl = str_replace( '{{DB}}', $dbname, $this->viewtpl );
56 |
57 | foreach( $schema['sequence'] ?? [] as $name => $def ) {
58 | $this->write( $this->path . $ds . $prefix . 'seq_' . $name . '.php', $this->sequence( $def, $seqtpl ) );
59 | }
60 |
61 | foreach( $schema['table'] ?? [] as $name => $def ) {
62 | $this->write( $this->path . $ds . $prefix . 'table_' . $name . '.php', $this->table( $def, $tabletpl ) );
63 | }
64 |
65 | foreach( $schema['view'] ?? [] as $name => $def ) {
66 | $this->write( $this->path . $ds . $prefix . 'view_' . $name . '.php', $this->view( $def, $viewtpl ) );
67 | }
68 | }
69 |
70 |
71 | /**
72 | * Returns the PHP code for an array
73 | *
74 | * @param array $a Associative list of array values
75 | * @return string PHP code for the array
76 | */
77 | protected function array( array $a ) : string
78 | {
79 | return str_replace( ['array (', ')', "\n", ' ', ',]'], ['[', ']', '', '', ']'], var_export( $a, true ) );
80 | }
81 |
82 |
83 | /**
84 | * Returns the PHP code for a column definition
85 | *
86 | * @param array $def Associative list of column definitions
87 | * @return string PHP code for the column definitions
88 | */
89 | protected function col( array $def ) : string
90 | {
91 | $lines = [];
92 | $types = [
93 | 'bigint', 'binary', 'blob', 'boolean',
94 | 'date', 'datetime', 'datetimetz',
95 | 'float', 'guid', 'integer', 'json',
96 | 'smallint', 'string', 'text', 'time'
97 | ];
98 |
99 | foreach( $def as $name => $e )
100 | {
101 | if( in_array( $e['type'], $types ) ) {
102 | $string = '$t->' . $e['type'] . '(\'' . $name . '\')';
103 | } else {
104 | $string = '$t->col(\'' . $name . '\', \'' . $e['type'] . '\')';
105 | }
106 |
107 | foreach( ['seq', 'fixed', 'unsigned', 'null'] as $key )
108 | {
109 | if( $e[$key] ?? false ) {
110 | $string .= '->' . $key . '(true)';
111 | }
112 | }
113 |
114 | foreach( ['length', 'precision', 'scale'] as $key )
115 | {
116 | if( $e[$key] ?? false ) {
117 | $string .= '->' . $key . '(' . $e[$key] . ')';
118 | }
119 | }
120 |
121 | foreach( ['default', 'comment'] as $key )
122 | {
123 | if( $e[$key] ?? false ) {
124 | $string .= '->' . $key . '(\'' . $e[$key] . '\')';
125 | }
126 | }
127 |
128 | foreach( $e['opt'] ?? [] as $key => $value ) {
129 | $string .= '->opt(\'' . $key . '\', \'' . $value . '\')';
130 | }
131 |
132 | $lines[] = $string . ';';
133 | }
134 |
135 | return join( "\n\t\t\t", $lines );
136 | }
137 |
138 |
139 | /**
140 | * Returns the PHP code for a foreign key definition
141 | *
142 | * @param array $def Associative list of foreign key definitions
143 | * @return string PHP code for the foreign key definitions
144 | */
145 | protected function foreign( array $def ) : string
146 | {
147 | $lines = [];
148 |
149 | foreach( $def as $name => $e )
150 | {
151 | $string = '$t->foreign(' . json_encode( $e['localcol'] ) . ', \'' . ( $e['fktable'] ?? '' ) . '\', ' . json_encode( $e['fkcol'] ) . ', ' . ( $e['name'] ? '\'' . $e['name'] . '\'' : 'null' ) . ')';
152 |
153 | foreach( ['onDelete', 'onUpdate'] as $key )
154 | {
155 | if( $e[$key] ?? false ) {
156 | $string .= '->' . $key . '(\'' . $e[$key] . '\')';
157 | }
158 | }
159 |
160 | $lines[] = $string . ';';
161 | }
162 |
163 | return join( "\n\t\t\t", $lines );
164 | }
165 |
166 |
167 | /**
168 | * Returns the PHP code for an index definition
169 | *
170 | * @param array $def Associative list of index definitions
171 | * @return string PHP code for the index definitions
172 | */
173 | protected function index( array $def ) : string
174 | {
175 | $lines = [];
176 |
177 | foreach( $def as $name => $e )
178 | {
179 | if( $e['primary'] ?? false ) {
180 | $lines[] = '$t->primary(' . json_encode( $e['columns'] ) . ', ' . ( $e['name'] ? '\'' . $e['name'] . '\'' : 'null' ) . ');';
181 | } elseif( $e['unique'] ?? false ) {
182 | $lines[] = '$t->unique(' . json_encode( $e['columns'] ) . ', ' . ( $e['name'] ? '\'' . $e['name'] . '\'' : 'null' ) . ');';
183 | } else {
184 | $lines[] = '$t->index(' . json_encode( $e['columns'] ?? [] ) . ', ' . ( $e['name'] ? '\'' . $e['name'] . '\'' : 'null' ) . ', ' . $this->array( $e['flags'] ?? [] ) . ', ' . $this->array( $e['options'] ?? [] ) . ');';
185 | }
186 | }
187 |
188 | return join( "\n\t\t\t", $lines );
189 | }
190 |
191 |
192 | /**
193 | * Reads the content of a file
194 | *
195 | * @param string $filename Name of the file
196 | * @return string Content of the file
197 | * @throws \RuntimeException If the file can't be read
198 | */
199 | protected function read( string $filename ) : string
200 | {
201 | if( ( $content = file_get_contents( $filename ) ) === false ) {
202 | throw new \RuntimeException( 'Unable to read from file "' . $filename . '"' );
203 | }
204 |
205 | return $content;
206 | }
207 |
208 |
209 | /**
210 | * Returns the PHP code for a sequence definition
211 | *
212 | * @param array $def Associative list of sequence definitions
213 | * @param string $template Template for the sequence definition
214 | * @return string PHP code for the sequence definitions
215 | */
216 | protected function sequence( array $def, string $template ) : string
217 | {
218 | $string = '';
219 |
220 | foreach( ['cache', 'start', 'step'] as $key )
221 | {
222 | if( $def[$key] ?? false ) {
223 | $string .= '->' . $key . '(' . $def[$key] . ')';
224 | }
225 | }
226 |
227 | if( $string ) {
228 | $string = '$s' . $string . ';';
229 | }
230 |
231 | return str_replace( ['{{NAME}}', '{{SEQUENCE}}'], [$def['name'] ?? '', $string], $template );
232 | }
233 |
234 |
235 | /**
236 | * Returns the PHP code for a table definition
237 | *
238 | * @param array $def Associative list of table definitions
239 | * @param string $template Template for the table definition
240 | * @return string PHP code for the table definitions
241 | */
242 | protected function table( array $def, string $template ) : string
243 | {
244 | $fktables = [];
245 |
246 | foreach( $def['foreign'] ?? [] as $e ) {
247 | $fktables[] = 'table_' . $e['fktable'] ?? '';
248 | }
249 |
250 | return str_replace( [
251 | '{{NAME}}',
252 | '{{COLUMN}}',
253 | '{{INDEX}}',
254 | '{{FOREIGN}}',
255 | '{{AFTER}}'
256 | ], [
257 | $def['name'] ?? '',
258 | $this->col( $def['col'] ?? [] ),
259 | $this->index( $def['index'] ?? [] ),
260 | $this->foreign( $def['foreign'] ?? [] ),
261 | $fktables ? "'" . join( "', '", $fktables ) . "'" : ''
262 | ], $template );
263 | }
264 |
265 |
266 | /**
267 | * Returns the PHP code for a view definition
268 | *
269 | * @param array $def Associative list of view definitions
270 | * @param string $template Template for the view definition
271 | * @return string PHP code for the view definitions
272 | */
273 | protected function view( array $def, string $template ) : string
274 | {
275 | return str_replace( ['{{NAME}}', '{{SQL}}'], [$def['name'] ?? '', $def['sql'] ?? ''], $template );
276 | }
277 |
278 |
279 | /**
280 | * Writes the content to the file
281 | *
282 | * @param string $filename Name of the file
283 | * @param string $content Content to write
284 | * @throws \RuntimeException If the file can't be written
285 | */
286 | protected function write( string $filename, string $content ) : void
287 | {
288 | if( file_put_contents( $filename, $content ) === false ) {
289 | throw new \RuntimeException( 'Unable to write to file "' . $filename . '"' );
290 | }
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # PHP CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-php/ for more details
4 | #
5 | version: 2
6 |
7 | jobs:
8 | "php-7.4 / mysql-5.7":
9 | docker:
10 | - image: aimeos/ci-php:7.4
11 | - image: cimg/mysql:5.7
12 | environment:
13 | MYSQL_ROOT_PASSWORD: rootpw
14 | MYSQL_DATABASE: upscheme
15 | MYSQL_USER: upscheme
16 | MYSQL_PASSWORD: upscheme
17 | steps:
18 | - checkout
19 | - restore_cache:
20 | keys:
21 | - php74-{{ checksum "composer.json" }}
22 | - run: composer update -n --prefer-dist
23 | - save_cache:
24 | key: php74-{{ checksum "composer.json" }}
25 | paths: [./vendor]
26 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
27 | - run: echo " 'pdo_mysql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
28 | - run: ./vendor/bin/phpunit
29 |
30 | "php-7.4 / mariadb-10":
31 | docker:
32 | - image: aimeos/ci-php:7.4
33 | - image: cimg/mariadb:10.4
34 | environment:
35 | MYSQL_ROOT_PASSWORD: rootpw
36 | MYSQL_DATABASE: upscheme
37 | MYSQL_USER: upscheme
38 | MYSQL_PASSWORD: upscheme
39 | steps:
40 | - checkout
41 | - restore_cache:
42 | keys:
43 | - php74-{{ checksum "composer.json" }}
44 | - run: composer update -n --prefer-dist
45 | - save_cache:
46 | key: php74-{{ checksum "composer.json" }}
47 | paths: [./vendor]
48 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
49 | - run: echo " 'pdo_mysql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
50 | - run: ./vendor/bin/phpunit
51 |
52 | "php-7.4 / pgsql-13":
53 | docker:
54 | - image: aimeos/ci-php:7.4
55 | - image: cimg/postgres:13.16
56 | environment:
57 | POSTGRES_PASSWORD: upscheme
58 | POSTGRES_USER: upscheme
59 | POSTGRES_DB: upscheme
60 | steps:
61 | - checkout
62 | - restore_cache:
63 | keys:
64 | - php74-{{ checksum "composer.json" }}
65 | - run: composer update -n --prefer-dist
66 | - save_cache:
67 | key: php74-{{ checksum "composer.json" }}
68 | paths: [./vendor]
69 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 5432 && echo OK && exit 0; echo -n .; sleep 1; done
70 | - run: echo " 'pdo_pgsql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
71 | - run: ./vendor/bin/phpunit
72 |
73 | "php-7.4 / mysql-8":
74 | docker:
75 | - image: aimeos/ci-php:7.4
76 | - image: cimg/mysql:8.0
77 | environment:
78 | MYSQL_ROOT_PASSWORD: rootpw
79 | MYSQL_DATABASE: upscheme
80 | MYSQL_USER: upscheme
81 | MYSQL_PASSWORD: upscheme
82 | steps:
83 | - checkout
84 | - restore_cache:
85 | keys:
86 | - php74-{{ checksum "composer.json" }}
87 | - run: composer update -n --prefer-dist
88 | - save_cache:
89 | key: php74-{{ checksum "composer.json" }}
90 | paths: [./vendor]
91 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
92 | - run: echo " 'pdo_mysql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
93 | - run: ./vendor/bin/phpunit
94 |
95 | "php-8.1 / oracle-18":
96 | docker:
97 | - image: aimeos/ci-php:8.1
98 | - image: gvenzl/oracle-xe:18-slim
99 | environment:
100 | ORACLE_PASSWORD: oracle
101 | steps:
102 | - checkout
103 | - restore_cache:
104 | keys:
105 | - php81-{{ checksum "composer.json" }}
106 | - run: composer update -n --prefer-dist
107 | - save_cache:
108 | key: php81-{{ checksum "composer.json" }}
109 | paths: [./vendor]
110 | - run: echo " 'pdo_oci', 'host' => '127.0.0.1', 'dbname' => 'XE', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
111 | - run: echo "CREATE USER upscheme IDENTIFIED BY upscheme;" > init.sql && echo "GRANT DBA TO upscheme;" >> init.sql && echo "quit" >> init.sql
112 | - run: for i in `seq 1 5`; do sqlplus -L system/oracle@127.0.0.1 @init.sql && exit 0; echo -n .; sleep 5; done
113 | - run: ./vendor/bin/phpunit || true
114 |
115 | "php-8.1 / mssql-2019":
116 | docker:
117 | - image: aimeos/ci-php:8.1
118 | - image: mcr.microsoft.com/mssql/server:2019-latest
119 | environment:
120 | SA_PASSWORD: S3c0r3P4ss
121 | ACCEPT_EULA: Y
122 | steps:
123 | - checkout
124 | - restore_cache:
125 | keys:
126 | - php81-{{ checksum "composer.json" }}
127 | - run: composer update -n --prefer-dist
128 | - save_cache:
129 | key: php81-{{ checksum "composer.json" }}
130 | paths: [./vendor]
131 | - run: echo "CREATE DATABASE upscheme;" > sqlserver.sql; for i in `seq 1 5`; do /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P 'S3c0r3P4ss' -i sqlserver.sql && exit 0; sleep 3; done
132 | - run: echo " 'pdo_sqlsrv', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'SA', 'password' => 'S3c0r3P4ss'];" > tests/config.php
133 | - run: ./vendor/bin/phpunit
134 |
135 | "php-8.2 / sqlite":
136 | docker:
137 | - image: aimeos/ci-php:8.2
138 | steps:
139 | - checkout
140 | - restore_cache:
141 | keys:
142 | - php82-{{ checksum "composer.json" }}
143 | - run: composer update -n --prefer-dist
144 | - save_cache:
145 | key: php82-{{ checksum "composer.json" }}
146 | paths: [./vendor]
147 | - run: echo " 'pdo_sqlite', 'path' => 'sqlite.test'];" > tests/config.php
148 | - run: ./vendor/bin/phpunit --coverage-clover coverage.xml --coverage-filter ./tests/
149 | - run: ./vendor/bin/php-coveralls -vvv -o coverage.json -x coverage.xml
150 |
151 | "php-8.3 / mariadb-11":
152 | docker:
153 | - image: aimeos/ci-php:8.3
154 | - image: cimg/mariadb:11.0
155 | environment:
156 | MYSQL_ROOT_PASSWORD: rootpw
157 | MYSQL_DATABASE: upscheme
158 | MYSQL_USER: upscheme
159 | MYSQL_PASSWORD: upscheme
160 | steps:
161 | - checkout
162 | - restore_cache:
163 | keys:
164 | - php83-{{ checksum "composer.json" }}
165 | - run: composer update -n --prefer-dist
166 | - save_cache:
167 | key: php83-{{ checksum "composer.json" }}
168 | paths: [./vendor]
169 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
170 | - run: echo " 'pdo_mysql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
171 | - run: ./vendor/bin/phpunit
172 |
173 | "php-8.4 / pgsql-18":
174 | docker:
175 | - image: aimeos/ci-php:8.4
176 | - image: cimg/postgres:18.0
177 | environment:
178 | POSTGRES_PASSWORD: upscheme
179 | POSTGRES_USER: upscheme
180 | POSTGRES_DB: upscheme
181 | steps:
182 | - checkout
183 | - restore_cache:
184 | keys:
185 | - php84-{{ checksum "composer.json" }}
186 | - run: composer update -n --prefer-dist
187 | - save_cache:
188 | key: php84-{{ checksum "composer.json" }}
189 | paths: [./vendor]
190 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 5432 && echo OK && exit 0; echo -n .; sleep 1; done
191 | - run: echo " 'pdo_pgsql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
192 | - run: ./vendor/bin/phpunit
193 |
194 | "php-8.5 / mysql-9":
195 | docker:
196 | - image: aimeos/ci-php:8.5
197 | - image: cimg/mysql:9.5
198 | environment:
199 | MYSQL_ROOT_PASSWORD: rootpw
200 | MYSQL_DATABASE: upscheme
201 | MYSQL_USER: upscheme
202 | MYSQL_PASSWORD: upscheme
203 | steps:
204 | - checkout
205 | - restore_cache:
206 | keys:
207 | - php85-{{ checksum "composer.json" }}
208 | - run: composer update -n --prefer-dist
209 | - save_cache:
210 | key: php85-{{ checksum "composer.json" }}
211 | paths: [./vendor]
212 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
213 | - run: echo " 'pdo_mysql', 'host' => '127.0.0.1', 'dbname' => 'upscheme', 'user' => 'upscheme', 'password' => 'upscheme'];" > tests/config.php
214 | - run: ./vendor/bin/phpunit
215 |
216 | workflows:
217 | version: 2
218 | unittest:
219 | jobs:
220 | - "php-7.4 / mysql-5.7"
221 | - "php-7.4 / mariadb-10"
222 | - "php-7.4 / pgsql-13"
223 | - "php-7.4 / mysql-8"
224 | - "php-8.1 / oracle-18"
225 | - "php-8.1 / mssql-2019"
226 | - "php-8.2 / sqlite"
227 | - "php-8.3 / mariadb-11"
228 | - "php-8.4 / pgsql-18"
229 | - "php-8.5 / mysql-9"
230 |
--------------------------------------------------------------------------------
/src/Up.php:
--------------------------------------------------------------------------------
1 | >
22 | */
23 | private $config;
24 |
25 | /**
26 | * @var array
27 | */
28 | private $tasks;
29 |
30 | /**
31 | * @var array
32 | */
33 | private $tasksDone;
34 |
35 | /**
36 | * @var array>
37 | */
38 | private $dependencies;
39 |
40 | /**
41 | * @var int
42 | */
43 | private $verbose = 0;
44 |
45 | /**
46 | * @var array
47 | */
48 | private $paths = [];
49 |
50 | /**
51 | * @var array
52 | */
53 | private $db = [];
54 |
55 |
56 | /**
57 | * Initializes the new object
58 | *
59 | * @param array> $config One or more database configuration parameters
60 | * @param array|string $paths One or more paths to the tasks which updates the database
61 | */
62 | public function __construct( array $config, $paths )
63 | {
64 | if( empty( $config ) ) {
65 | throw new \RuntimeException( 'No database configuration passed' );
66 | }
67 |
68 | if( empty( $paths ) ) {
69 | throw new \RuntimeException( 'No path for the tasks passed' );
70 | }
71 |
72 | if( spl_autoload_register( static::macro( 'autoload' ) ?: [$this, 'autoload'] ) === false ) {
73 | throw new \RuntimeException( 'Unable to register autoloader' );
74 | }
75 |
76 | $this->config = $config;
77 | $this->paths = (array) $paths;
78 |
79 | $this->call( 'macros' );
80 | }
81 |
82 |
83 | /**
84 | * Creates a new Upscheme object initialized with the given configuration and paths
85 | *
86 | * @param array> $config One or more database configuration parameters
87 | * @param array|string $paths One or more paths to the tasks which updates the database
88 | * @return \Aimeos\Upscheme\Up Upscheme object
89 | */
90 | public static function use( array $config, $paths ) : self
91 | {
92 | return new self( $config, $paths );
93 | }
94 |
95 |
96 | /**
97 | * Creates the database migration files for the given connection name
98 | *
99 | * @param string|array $name Name of the connection from the configuration or empty string for first one
100 | * @return self Same object for fluid method calls
101 | */
102 | public function create( $name = '' ) : self
103 | {
104 | $path = current( $this->paths );
105 | $generator = ( $fcn = static::macro( 'generate' ) ) ? $fcn( $path ) : new Generate( $path );
106 |
107 | foreach( (array) $name as $dbname ) {
108 | $generator( $this->db( $dbname )->toArray(), $dbname );
109 | }
110 |
111 | return $this;
112 | }
113 |
114 |
115 | /**
116 | * Returns the DB schema for the passed connection name
117 | *
118 | * @param string $name Name of the connection from the configuration or empty string for first one
119 | * @param bool $new If a new connection should be created instead of reusing an existing one
120 | * @return \Aimeos\Upscheme\Schema\DB DB schema object
121 | */
122 | public function db( string $name = '', bool $new = false ) : \Aimeos\Upscheme\Schema\DB
123 | {
124 | if( !isset( $this->config[$name] ) ) {
125 | $cfg = is_array( $first = reset( $this->config ) ) ? $first : $this->config; $name = '';
126 | } else {
127 | $cfg = $this->config[$name];
128 | }
129 |
130 | if( !isset( $this->db[$name] ) ) {
131 | $this->db[$name] = new \Aimeos\Upscheme\Schema\DB( $this, $this->connect( $cfg ) );
132 | }
133 |
134 | return $new ? clone $this->db[$name] : $this->db[$name];
135 | }
136 |
137 |
138 | /**
139 | * Outputs the message depending on the passed verbosity level
140 | *
141 | * @param string $msg Message to display
142 | * @param mixed $level Verbosity level (empty: always, v: notice: vv: info, vvv: debug)
143 | * @return self Same object for fluid method calls
144 | */
145 | public function info( string $msg, $level = 'v' ) : self
146 | {
147 | if( $fcn = static::macro( 'info' ) ) {
148 | $fcn( $msg, $level );
149 | } elseif( strlen( (string) $level ) <= $this->verbose ) {
150 | echo $msg . PHP_EOL;
151 | }
152 |
153 | return $this;
154 | }
155 |
156 |
157 | /**
158 | * Returns the paths for the setup tasks
159 | *
160 | * @return array List of paths
161 | */
162 | public function paths() : array
163 | {
164 | return $this->paths;
165 | }
166 |
167 |
168 | /**
169 | * Executes the tasks to update the database
170 | *
171 | * @return self Same object for fluid method calls
172 | */
173 | public function up() : self
174 | {
175 | $this->tasksDone = [];
176 | $this->dependencies = [];
177 | $this->tasks = $this->createTasks( $this->paths() );
178 |
179 | foreach( $this->tasks as $name => $task )
180 | {
181 | foreach( (array) $task->before() as $taskname ) {
182 | $this->dependencies[$taskname][] = $name;
183 | }
184 |
185 | foreach( (array) $task->after() as $taskname ) {
186 | $this->dependencies[$name][] = $taskname;
187 | }
188 | }
189 |
190 | foreach( $this->tasks as $taskname => $task ) {
191 | $this->runTasks( [$taskname] );
192 | }
193 |
194 | return $this;
195 | }
196 |
197 |
198 | /**
199 | * Sets the verbosity level
200 | *
201 | * @param mixed $level Verbosity level (empty: none, v: notice: vv: info, vvv: debug)
202 | * @return self Same object for fluid method calls
203 | */
204 | public function verbose( $level = 'v' ) : self
205 | {
206 | $this->verbose = ( $fcn = static::macro( 'verbose' ) ) ? $fcn( $level ) : strlen( (string) $level );
207 | return $this;
208 | }
209 |
210 |
211 | /**
212 | * Autoloader for setup tasks.
213 | *
214 | * @param string $classname Name of the class to load
215 | * @return bool True if class was found, false if not
216 | */
217 | protected function autoload( string $classname ) : bool
218 | {
219 | if( !strncmp( $classname, 'Aimeos\Upscheme\Task\\', 21 ) )
220 | {
221 | $fileName = substr( $classname, 21 ) . '.php';
222 |
223 | foreach( $this->paths() as $path )
224 | {
225 | $file = $path . '/' . $fileName;
226 |
227 | if( file_exists( $file ) === true && ( include_once $file ) !== false ) {
228 | return true;
229 | }
230 | }
231 | }
232 |
233 | return false;
234 | }
235 |
236 |
237 | /**
238 | * Creates a new database connection from the given configuration
239 | *
240 | * @param array $cfg Database configuration
241 | * @return \Doctrine\DBAL\Connection New DBAL database connection
242 | */
243 | protected function connect( array $cfg ) : \Doctrine\DBAL\Connection
244 | {
245 | $cfg['driverOptions'][\PDO::ATTR_CASE] = \PDO::CASE_NATURAL;
246 | $cfg['driverOptions'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION;
247 | $cfg['driverOptions'][\PDO::ATTR_ORACLE_NULLS] = \PDO::NULL_NATURAL;
248 | $cfg['driverOptions'][\PDO::ATTR_STRINGIFY_FETCHES] = false;
249 |
250 | if( $fcn = static::macro( 'connect' ) ) {
251 | return $fcn( $cfg );
252 | }
253 |
254 | $dbalconf = new \Doctrine\DBAL\Configuration();
255 | $dbalconf->setSchemaManagerFactory( new \Doctrine\DBAL\Schema\DefaultSchemaManagerFactory() );
256 | $conn = \Doctrine\DBAL\DriverManager::getConnection( $cfg, $dbalconf );
257 |
258 | if( in_array( $cfg['driver'], ['oci8', 'pdo_oci'] ) ) {
259 | $conn->executeStatement( "ALTER SESSION SET NLS_TIME_FORMAT='HH24:MI:SS' NLS_DATE_FORMAT='YYYY-MM-DD' NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_TZ_FORMAT='YYYY-MM-DD HH24:MI:SS TZH:TZM' NLS_NUMERIC_CHARACTERS='.,'" );
260 | }
261 |
262 | return $conn;
263 | }
264 |
265 |
266 | /**
267 | * Creates the tasks from the given directories
268 | *
269 | * @param array $paths List of paths containing task classes
270 | * @return array<\Aimeos\Upscheme\Task\Iface> List of task objects
271 | */
272 | protected function createTasks( array $paths ) : array
273 | {
274 | $tasks = [];
275 |
276 | foreach( $paths as $path )
277 | {
278 | foreach( new \DirectoryIterator( $path ) as $item )
279 | {
280 | if( $item->isDir() === true || substr( $item->getFilename(), -4 ) != '.php' ) { continue; }
281 |
282 | $interface = \Aimeos\Upscheme\Task\Iface::class;
283 | $taskname = substr( $item->getFilename(), 0, -4 );
284 |
285 | if( !is_object( $task = include_once $item->getPathName() ) )
286 | {
287 | $classname = '\Aimeos\Upscheme\Task\\' . $taskname;
288 |
289 | if( class_exists( $classname ) === false ) {
290 | throw new \RuntimeException( sprintf( 'Class "%1$s" not found', $classname ) );
291 | }
292 |
293 | $task = ( $fcn = static::macro( 'createTask' ) ) ? $fcn( $classname ) : new $classname( $this );
294 | }
295 |
296 | if( ( $task instanceof $interface ) === false ) {
297 | throw new \RuntimeException( sprintf( 'Class "%1$s" doesn\'t implement "%2$s"', $classname, $interface ) );
298 | }
299 |
300 | $task->_filename = $item->getPathName();
301 | $tasks[$taskname] = $task;
302 | }
303 | }
304 |
305 | ksort( $tasks );
306 | return $tasks;
307 | }
308 |
309 |
310 | /**
311 | * Adds default macros which can be overwritten
312 | */
313 | protected function macros()
314 | {
315 | \Aimeos\Upscheme\Schema\Table::macro( 'id', function( ?string $name = null ) : Schema\Column {
316 | return $this->integer( $name ?: 'id' )->seq( true )->primary();
317 | } );
318 |
319 | \Aimeos\Upscheme\Schema\Table::macro( 'bigid', function( ?string $name = null ) : Schema\Column {
320 | return $this->bigint( $name ?: 'id' )->seq( true )->primary();
321 | } );
322 | }
323 |
324 |
325 | /**
326 | * Executes each task depending of the task dependencies
327 | *
328 | * @param array $tasknames List of task names
329 | * @param array $stack List of task names that are scheduled after this task
330 | */
331 | protected function runTasks( array $tasknames, array $stack = [] ) : void
332 | {
333 | $dir = getcwd();
334 | $dirlen = strlen( $dir );
335 |
336 | foreach( $tasknames as $taskname )
337 | {
338 | if( in_array( $taskname, $this->tasksDone ) ) {
339 | continue;
340 | }
341 |
342 | if( in_array( $taskname, $stack ) )
343 | {
344 | $msg = 'Circular dependency for "%1$s" detected. Task stack: %2$s';
345 | throw new \RuntimeException( sprintf( $msg, $taskname, join( ', ', $stack ) ) );
346 | }
347 |
348 | $stack[] = $taskname;
349 |
350 | if( isset( $this->dependencies[$taskname] ) ) {
351 | $this->runTasks( (array) $this->dependencies[$taskname], $stack );
352 | }
353 |
354 | if( isset( $this->tasks[$taskname] ) )
355 | {
356 | $start = microtime( true );
357 | $file = $this->tasks[$taskname]->_filename;
358 |
359 | if( !strncmp( $file, $dir, $dirlen ) ) {
360 | $file = ltrim( substr( $file, $dirlen ), '/' );
361 | }
362 |
363 | $this->info( 'Migrating: ' . $file, 'v' );
364 | $this->tasks[$taskname]->up();
365 |
366 | foreach( $this->db as $db ) {
367 | $db->up();
368 | }
369 |
370 | $this->info( 'Migrated: ' . $file . ' (' . round( ( microtime( true ) - $start ) * 1000, 2 ) . 'ms)', 'v' );
371 | }
372 | else
373 | {
374 | $this->info( 'Missing: ' . $taskname, 'v' );
375 | }
376 |
377 | $this->tasksDone[] = $taskname;
378 | }
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/src/Schema/Column.php:
--------------------------------------------------------------------------------
1 | db = $db;
48 | $this->table = $table;
49 | $this->column = $column;
50 | }
51 |
52 |
53 | /**
54 | * Calls custom methods or passes unknown method calls to the Doctrine column object
55 | *
56 | * @param string $method Name of the method
57 | * @param array $args Method parameters
58 | * @return mixed Return value of the called method
59 | */
60 | public function __call( string $method, array $args )
61 | {
62 | if( self::macro( $method ) ) {
63 | return $this->call( $method, ...$args );
64 | }
65 |
66 | return $this->column->{$method}( ...$args );
67 | }
68 |
69 |
70 | /**
71 | * Returns the value for the given column option
72 | *
73 | * @param string $name Column option name
74 | * @return mixed Column option value
75 | */
76 | public function __get( string $name )
77 | {
78 | return $this->opt( $name );
79 | }
80 |
81 |
82 | /**
83 | * Sets the new value for the given column option
84 | *
85 | * @param string $name Column option name
86 | * @param mixed $value Column option value
87 | */
88 | public function __set( string $name, $value )
89 | {
90 | $this->opt( $name, $value );
91 | }
92 |
93 |
94 | /**
95 | * Sets the column option value or returns the current value
96 | *
97 | * @param string $option Column option name
98 | * @param mixed $value New column option value or NULL to return current value
99 | * @param array|string|null $for Database type this option should be used for ("mysql", "postgresql", "sqlite", "mssql", "oracle", "db2")
100 | * @return self|mixed Same object for setting the value, current value without parameter
101 | */
102 | public function opt( string $option, $value = null, $for = null )
103 | {
104 | if( $value === null ) {
105 | return $this->column->getPlatformOption( $option );
106 | }
107 |
108 | if( $for === null || in_array( $this->db->type(), (array) $for ) ) {
109 | $this->column->setPlatformOption( $option, $value );
110 | }
111 |
112 | return $this;
113 | }
114 |
115 |
116 | /**
117 | * Sets the column as autoincrement or returns the current value
118 | *
119 | * This method is an alias for seq().
120 | *
121 | * @param bool|null $value New autoincrement flag or NULL to return current value
122 | * @return self|bool Same object for setting the value, current value without parameter
123 | */
124 | public function autoincrement( ?bool $value = null )
125 | {
126 | return $this->seq( $value );
127 | }
128 |
129 |
130 | /**
131 | * Sets the column charset or returns the current value
132 | *
133 | * @param string|null $value New column charset or NULL to return current value
134 | * @return self|string Same object for setting the value, current value without parameter
135 | */
136 | public function charset( ?string $value = null )
137 | {
138 | return $this->opt( 'charset', $value );
139 | }
140 |
141 |
142 | /**
143 | * Sets the column collation or returns the current value
144 | *
145 | * @param string|null $value New column collation or NULL to return current value
146 | * @return self|string Same object for setting the value, current value without parameter
147 | */
148 | public function collation( ?string $value = null )
149 | {
150 | return $this->opt( 'collation', $value );
151 | }
152 |
153 |
154 | /**
155 | * Sets the column comment or returns the current value
156 | *
157 | * @param string|null $value New column comment or NULL to return current value
158 | * @return self|string Same object for setting the value, current value without parameter
159 | */
160 | public function comment( ?string $value = null )
161 | {
162 | if( $value === null ) {
163 | return $this->column->getComment();
164 | }
165 |
166 | $this->column->setComment( $value );
167 | return $this;
168 | }
169 |
170 |
171 | /**
172 | * Sets the custom column definition or returns the current value
173 | *
174 | * @param string $value Custom column definition
175 | * @param array|string|null $for Database type this option should be used for ("mysql", "postgresql", "sqlite", "mssql", "oracle", "db2")
176 | * @return \Aimeos\Upscheme\Schema\Column Column object
177 | */
178 | public function custom( ?string $value = null, $for = null )
179 | {
180 | if( $value === null ) {
181 | return $this->column->getColumnDefinition();
182 | }
183 |
184 | if( $for === null || in_array( $this->db->type(), (array) $for ) ) {
185 | $this->column->setColumnDefinition( $value );
186 | }
187 |
188 | return $this;
189 | }
190 |
191 |
192 | /**
193 | * Sets the column default value or returns the current value
194 | *
195 | * @param mixed $value New column default value or NULL to return current value
196 | * @return self|mixed Same object for setting the value, current value without parameter
197 | */
198 | public function default( $value = null )
199 | {
200 | if( $value === null ) {
201 | return $this->column->getDefault();
202 | }
203 |
204 | $this->column->setDefault( $value );
205 | return $this;
206 | }
207 |
208 |
209 | /**
210 | * Sets the column fixed flag or returns the current value
211 | *
212 | * @param bool|null $value New column fixed flag or NULL to return current value
213 | * @return self|bool Same object for setting the value, current value without parameter
214 | */
215 | public function fixed( ?bool $value = null )
216 | {
217 | if( $value === null ) {
218 | return $this->column->getFixed();
219 | }
220 |
221 | $this->column->setFixed( $value );
222 | return $this;
223 | }
224 |
225 |
226 | /**
227 | * Sets the column length or returns the current value
228 | *
229 | * @param int|null $value New column length or NULL to return current value
230 | * @return self|int Same object for setting the value, current value without parameter
231 | */
232 | public function length( ?int $value = null )
233 | {
234 | if( $value === null ) {
235 | return $this->column->getLength();
236 | }
237 |
238 | $this->column->setLength( $value );
239 | return $this;
240 | }
241 |
242 |
243 | /**
244 | * Returns the name of the column
245 | *
246 | * @return string Column name
247 | */
248 | public function name() : string
249 | {
250 | return $this->column->getName();
251 | }
252 |
253 |
254 | /**
255 | * Sets the column null flag or returns the current value
256 | *
257 | * @param bool|null $value New column null flag or NULL to return current value
258 | * @return self|bool Same object for setting the value, current value without parameter
259 | */
260 | public function null( ?bool $value = null )
261 | {
262 | if( $value === null ) {
263 | return !$this->column->getNotnull();
264 | }
265 |
266 | $this->column->setNotnull( !$value );
267 | return $this;
268 | }
269 |
270 |
271 | /**
272 | * Sets the column precision or returns the current value
273 | *
274 | * @param int|null $value New column precision value or NULL to return current value
275 | * @return self|int Same object for setting the value, current value without parameter
276 | */
277 | public function precision( ?int $value = null )
278 | {
279 | if( $value === null ) {
280 | return $this->column->getPrecision();
281 | }
282 |
283 | $this->column->setPrecision( $value );
284 | return $this;
285 | }
286 |
287 |
288 | /**
289 | * Sets the column scale or returns the current value
290 | *
291 | * @param int|null $value New column scale value or NULL to return current value
292 | * @return self|int Same object for setting the value, current value without parameter
293 | */
294 | public function scale( ?int $value = null )
295 | {
296 | if( $value === null ) {
297 | return $this->column->getScale();
298 | }
299 |
300 | $this->column->setScale( $value );
301 | return $this;
302 | }
303 |
304 |
305 | /**
306 | * Sets the column as autoincrement or returns the current value
307 | *
308 | * @param bool|null $value New autoincrement flag or NULL to return current value
309 | * @return self|bool Same object for setting the value, current value without parameter
310 | */
311 | public function seq( ?bool $value = null )
312 | {
313 | if( $value === null ) {
314 | return $this->column->getAutoincrement();
315 | }
316 |
317 | $this->column->setAutoincrement( $value );
318 | return $this;
319 | }
320 |
321 |
322 | /**
323 | * Sets the column type or returns the current value
324 | *
325 | * @param string|null $value New column type or NULL to return current value
326 | * @return self|string Same object for setting the value, current value without parameter
327 | */
328 | public function type( ?string $value = null )
329 | {
330 | if( $value === null )
331 | {
332 | $type = $this->column->getType();
333 | return \Doctrine\DBAL\Types\Type::lookupName( $type );
334 | }
335 |
336 | $this->column->setType( \Doctrine\DBAL\Types\Type::getType( $value ) );
337 | return $this;
338 | }
339 |
340 |
341 | /**
342 | * Sets the column unsigned flag or returns the current value
343 | *
344 | * @param bool|null $value New column unsigned flag or NULL to return current value
345 | * @return self|bool Same object for setting the value, current value without parameter
346 | */
347 | public function unsigned( ?bool $value = null )
348 | {
349 | if( $value === null ) {
350 | return $this->column->getUnsigned();
351 | }
352 |
353 | $this->column->setUnsigned( $value );
354 | return $this;
355 | }
356 |
357 |
358 | /**
359 | * Creates a regular index for the column
360 | *
361 | * @param string|null $name Name of the index or NULL to generate automatically
362 | * @return self Same object for fluid method calls
363 | */
364 | public function index( ?string $name = null ) : self
365 | {
366 | $this->table->index( [$this->name()], $name );
367 | return $this;
368 | }
369 |
370 |
371 | /**
372 | * Creates a primary index for the column
373 | *
374 | * @param string|null $name Name of the index or NULL to generate automatically
375 | * @return self Same object for fluid method calls
376 | */
377 | public function primary( ?string $name = null ) : self
378 | {
379 | $this->table->primary( [$this->name()], $name );
380 | return $this;
381 | }
382 |
383 |
384 | /**
385 | * Creates a spatial index for the column
386 | *
387 | * @param string|null $name Name of the index or NULL to generate automatically
388 | * @return self Same object for fluid method calls
389 | */
390 | public function spatial( ?string $name = null ) : self
391 | {
392 | $this->table->spatial( [$this->name()], $name );
393 | return $this;
394 | }
395 |
396 |
397 | /**
398 | * Creates an unique index for the column
399 | *
400 | * @param string|null $name Name of the index or NULL to generate automatically
401 | * @return self Same object for fluid method calls
402 | */
403 | public function unique( ?string $name = null ) : self
404 | {
405 | $this->table->unique( $this->name(), $name );
406 | return $this;
407 | }
408 |
409 |
410 | /**
411 | * Applies the changes to the database schema
412 | *
413 | * @return self Same object for fluid method calls
414 | */
415 | public function up() : self
416 | {
417 | $this->table->up();
418 | return $this;
419 | }
420 | }
--------------------------------------------------------------------------------
/tests/Upscheme/Schema/ColumnTest.php:
--------------------------------------------------------------------------------
1 | dbmock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
22 | ->disableOriginalConstructor()
23 | ->getMock();
24 |
25 | $this->tablemock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Table' )
26 | ->disableOriginalConstructor()
27 | ->getMock();
28 |
29 | $this->colmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Column' )
30 | ->disableOriginalConstructor()
31 | ->getMock();
32 |
33 | $this->object = new \Aimeos\Upscheme\Schema\Column( $this->dbmock, $this->tablemock, $this->colmock );
34 | }
35 |
36 |
37 | protected function tearDown() : void
38 | {
39 | unset( $this->object, $this->colmock, $this->tablemock, $this->dbmock );
40 | }
41 |
42 |
43 | public function testCall()
44 | {
45 | $this->colmock->expects( $this->once() )->method( 'getComment' );
46 |
47 | $this->object->getComment();
48 | }
49 |
50 |
51 | public function testCallMacro()
52 | {
53 | \Aimeos\Upscheme\Schema\Column::macro( 'unittest', function() { return 'yes'; } );
54 |
55 | $this->assertEquals( 'yes', $this->object->unittest() );
56 | }
57 |
58 |
59 | public function testGetMagic()
60 | {
61 | $this->colmock->expects( $this->once() )->method( 'getPlatformOption' )
62 | ->willReturn( 'yes' );
63 |
64 | $this->assertEquals( 'yes', $this->object->unittest );
65 | }
66 |
67 |
68 | public function testSetMagic()
69 | {
70 | $this->colmock->expects( $this->once() )->method( 'setPlatformOption' );
71 |
72 | $this->object->unittest = 'yes';
73 | }
74 |
75 |
76 | public function testOptGet()
77 | {
78 | $this->colmock->expects( $this->once() )->method( 'getPlatformOption' )
79 | ->willReturn( 'yes' );
80 |
81 | $this->assertEquals( 'yes', $this->object->opt( 'unittest' ) );
82 | }
83 |
84 |
85 | public function testOptSet()
86 | {
87 | $this->colmock->expects( $this->once() )->method( 'setPlatformOption' );
88 |
89 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->opt( 'unittest', 'yes' ) );
90 | }
91 |
92 |
93 | public function testOptSetType()
94 | {
95 | $this->dbmock->expects( $this->once() )->method( 'type' )->willReturn( 'mydb' );
96 | $this->colmock->expects( $this->once() )->method( 'setPlatformOption' );
97 |
98 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->opt( 'unittest', 'yes', 'mydb' ) );
99 | }
100 |
101 |
102 | public function testOptSetTypeNot()
103 | {
104 | $this->dbmock->expects( $this->once() )->method( 'type' )->willReturn( 'mydb' );
105 | $this->colmock->expects( $this->never() )->method( 'setPlatformOption' );
106 |
107 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->opt( 'unittest', 'yes', 'yourdb' ) );
108 | }
109 |
110 |
111 | public function testAutoincrementGet()
112 | {
113 | $this->colmock->expects( $this->once() )->method( 'getAutoincrement' )
114 | ->willReturn( true );
115 |
116 | $this->assertEquals( true, $this->object->autoincrement() );
117 | }
118 |
119 |
120 | public function testAutoincrementSet()
121 | {
122 | $this->colmock->expects( $this->once() )->method( 'setAutoincrement' );
123 |
124 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->autoincrement( true ) );
125 | }
126 |
127 |
128 | public function testCharsetGet()
129 | {
130 | $this->colmock->expects( $this->once() )->method( 'getPlatformOption' )
131 | ->willReturn( 'utf8' );
132 |
133 | $this->assertEquals( 'utf8', $this->object->charset() );
134 | }
135 |
136 |
137 | public function testCharsetSet()
138 | {
139 | $this->colmock->expects( $this->once() )->method( 'setPlatformOption' );
140 |
141 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->charset( 'utf8' ) );
142 | }
143 |
144 |
145 | public function testCollationGet()
146 | {
147 | $this->colmock->expects( $this->once() )->method( 'getPlatformOption' )
148 | ->willReturn( 'binary' );
149 |
150 | $this->assertEquals( 'binary', $this->object->collation() );
151 | }
152 |
153 |
154 | public function testCollationSet()
155 | {
156 | $this->colmock->expects( $this->once() )->method( 'setPlatformOption' );
157 |
158 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->collation( 'binary' ) );
159 | }
160 |
161 |
162 | public function testCommentGet()
163 | {
164 | $this->colmock->expects( $this->once() )->method( 'getComment' )
165 | ->willReturn( 'yes' );
166 |
167 | $this->assertEquals( 'yes', $this->object->comment() );
168 | }
169 |
170 |
171 | public function testCommentSet()
172 | {
173 | $this->colmock->expects( $this->once() )->method( 'setComment' );
174 |
175 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->comment( 'yes' ) );
176 | }
177 |
178 |
179 | public function testCustomGet()
180 | {
181 | $this->colmock->expects( $this->once() )->method( 'getColumnDefinition' )
182 | ->willReturn( 'INT NOT NULL' );
183 |
184 | $this->assertEquals( 'INT NOT NULL', $this->object->custom() );
185 | }
186 |
187 |
188 | public function testCustomSet()
189 | {
190 | $this->colmock->expects( $this->once() )->method( 'setColumnDefinition' );
191 |
192 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->custom( 'INT NOT NULL' ) );
193 | }
194 |
195 |
196 | public function testCustomSetType()
197 | {
198 | $this->dbmock->expects( $this->once() )->method( 'type' )->willReturn( 'mydb' );
199 | $this->colmock->expects( $this->once() )->method( 'setColumnDefinition' );
200 |
201 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->custom( 'INT NOT NULL', 'mydb' ) );
202 | }
203 |
204 |
205 | public function testCustomSetTypeNot()
206 | {
207 | $this->dbmock->expects( $this->once() )->method( 'type' )->willReturn( 'mydb' );
208 | $this->colmock->expects( $this->never() )->method( 'setColumnDefinition' );
209 |
210 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->custom( 'INT NOT NULL', 'yourdb' ) );
211 | }
212 |
213 |
214 | public function testDefaultGet()
215 | {
216 | $this->colmock->expects( $this->once() )->method( 'getDefault' )
217 | ->willReturn( 'yes' );
218 |
219 | $this->assertEquals( 'yes', $this->object->default() );
220 | }
221 |
222 |
223 | public function testDefaultSet()
224 | {
225 | $this->colmock->expects( $this->once() )->method( 'setDefault' );
226 |
227 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->default( 'yes' ) );
228 | }
229 |
230 |
231 | public function testFixedGet()
232 | {
233 | $this->colmock->expects( $this->once() )->method( 'getFixed' )
234 | ->willReturn( true );
235 |
236 | $this->assertEquals( true, $this->object->fixed() );
237 | }
238 |
239 |
240 | public function testFixedSet()
241 | {
242 | $this->colmock->expects( $this->once() )->method( 'setFixed' );
243 |
244 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->fixed( true ) );
245 | }
246 |
247 |
248 | public function testLengthGet()
249 | {
250 | $this->colmock->expects( $this->once() )->method( 'getLength' )
251 | ->willReturn( 10 );
252 |
253 | $this->assertEquals( 10, $this->object->length() );
254 | }
255 |
256 |
257 | public function testLengthSet()
258 | {
259 | $this->colmock->expects( $this->once() )->method( 'setLength' );
260 |
261 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->length( 10 ) );
262 | }
263 |
264 |
265 | public function testName()
266 | {
267 | $this->colmock->expects( $this->once() )->method( 'getName' )
268 | ->willReturn( 'unittest' );
269 |
270 | $this->assertEquals( 'unittest', $this->object->name() );
271 | }
272 |
273 |
274 | public function testNullGet()
275 | {
276 | $this->colmock->expects( $this->once() )->method( 'getNotnull' )
277 | ->willReturn( true );
278 |
279 | $this->assertEquals( false, $this->object->null() );
280 | }
281 |
282 |
283 | public function testNullSet()
284 | {
285 | $this->colmock->expects( $this->once() )->method( 'setNotnull' );
286 |
287 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->null( true ) );
288 | }
289 |
290 |
291 | public function testPrecisionGet()
292 | {
293 | $this->colmock->expects( $this->once() )->method( 'getPrecision' )
294 | ->willReturn( 10 );
295 |
296 | $this->assertEquals( 10, $this->object->precision() );
297 | }
298 |
299 |
300 | public function testPrecisionSet()
301 | {
302 | $this->colmock->expects( $this->once() )->method( 'setPrecision' );
303 |
304 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->precision( 10 ) );
305 | }
306 |
307 |
308 | public function testScaleGet()
309 | {
310 | $this->colmock->expects( $this->once() )->method( 'getScale' )
311 | ->willReturn( 10 );
312 |
313 | $this->assertEquals( 10, $this->object->scale() );
314 | }
315 |
316 |
317 | public function testScaleSet()
318 | {
319 | $this->colmock->expects( $this->once() )->method( 'setScale' );
320 |
321 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->scale( 10 ) );
322 | }
323 |
324 |
325 | public function testSeqGet()
326 | {
327 | $this->colmock->expects( $this->once() )->method( 'getAutoincrement' )
328 | ->willReturn( true );
329 |
330 | $this->assertEquals( true, $this->object->seq() );
331 | }
332 |
333 |
334 | public function testSeqSet()
335 | {
336 | $this->colmock->expects( $this->once() )->method( 'setAutoincrement' );
337 |
338 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->seq( true ) );
339 | }
340 |
341 |
342 | public function testTypeGet()
343 | {
344 | $this->colmock->expects( $this->once() )->method( 'getType' )
345 | ->willReturn( \Doctrine\DBAL\Types\Type::getType( 'string' ) );
346 |
347 | $this->assertEquals( 'string', $this->object->type() );
348 | }
349 |
350 |
351 | public function testTypeSet()
352 | {
353 | $this->colmock->expects( $this->once() )->method( 'setType' );
354 |
355 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->type( 'string' ) );
356 | }
357 |
358 |
359 | public function testUnsignedGet()
360 | {
361 | $this->colmock->expects( $this->once() )->method( 'getUnsigned' )
362 | ->willReturn( true );
363 |
364 | $this->assertEquals( true, $this->object->unsigned() );
365 | }
366 |
367 |
368 | public function testUnsignedSet()
369 | {
370 | $this->colmock->expects( $this->once() )->method( 'setUnsigned' );
371 |
372 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->unsigned( true ) );
373 | }
374 |
375 |
376 | public function testIndex()
377 | {
378 | $this->colmock->expects( $this->once() )->method( 'getName' )->willReturn( 'unitcol' );
379 | $this->tablemock->expects( $this->once() )->method( 'index' );
380 |
381 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->index( 'unittest', 'idx_utst' ) );
382 | }
383 |
384 |
385 | public function testPrimary()
386 | {
387 | $this->colmock->expects( $this->once() )->method( 'getName' )->willReturn( 'unitcol' );
388 | $this->tablemock->expects( $this->once() )->method( 'primary' );
389 |
390 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->primary( 'unittest', 'pk_utst' ) );
391 | }
392 |
393 |
394 | public function testSpatial()
395 | {
396 | $this->colmock->expects( $this->once() )->method( 'getName' )->willReturn( 'unitcol' );
397 | $this->tablemock->expects( $this->once() )->method( 'spatial' );
398 |
399 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->spatial( 'unittest', 'sp_utst' ) );
400 | }
401 |
402 |
403 | public function testUnique()
404 | {
405 | $this->colmock->expects( $this->once() )->method( 'getName' )->willReturn( 'unitcol' );
406 | $this->tablemock->expects( $this->once() )->method( 'unique' );
407 |
408 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->unique( 'unittest', 'unq_utst' ) );
409 | }
410 |
411 |
412 | public function testUp()
413 | {
414 | $this->tablemock->expects( $this->once() )->method( 'up' );
415 |
416 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $this->object->up() );
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/tests/Upscheme/Schema/DBTest.php:
--------------------------------------------------------------------------------
1 | upmock = $this->getMockBuilder( '\Aimeos\Upscheme\Up' )
25 | ->disableOriginalConstructor()
26 | ->getMock();
27 |
28 | $this->connmock = $this->getMockBuilder( '\Doctrine\DBAL\Connection' )
29 | ->disableOriginalConstructor()
30 | ->getMock();
31 |
32 | $this->pfmock = $this->getMockBuilder( '\Doctrine\DBAL\Platforms\MySQLPlatform' )
33 | ->disableOriginalConstructor()
34 | ->getMock();
35 |
36 | $this->smmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\AbstractSchemaManager' )
37 | ->disableOriginalConstructor()
38 | ->getMock();
39 |
40 | $this->schemamock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Schema' )
41 | ->disableOriginalConstructor()
42 | ->disableOriginalClone()
43 | ->getMock();
44 |
45 | $this->tablemock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Table' )
46 | ->disableOriginalConstructor()
47 | ->getMock();
48 |
49 |
50 | $this->connmock->expects( $this->any() )->method( 'createSchemaManager' )
51 | ->willReturn( $this->smmock );
52 |
53 | $this->connmock->expects( $this->any() )->method( 'quoteIdentifier' )
54 | ->willReturnCallback( function( $value ) {
55 | return '"' . $value . '"';
56 | } );
57 |
58 | $this->connmock->expects( $this->any() )->method( 'getDatabasePlatform' )
59 | ->willReturn( $this->pfmock );
60 |
61 | $this->smmock->expects( $this->any() )->method( 'introspectSchema' )
62 | ->willReturn( $this->schemamock );
63 |
64 |
65 | $this->object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
66 | ->setConstructorArgs( [$this->upmock, $this->connmock] )
67 | ->onlyMethods( ['table', 'up', 'getColumnSQL', 'getViews'] )
68 | ->getMock();
69 |
70 | $this->object->expects( $this->any() )->method( 'table' )->willReturn( $this->tablemock );
71 | }
72 |
73 |
74 | protected function tearDown() : void
75 | {
76 | unset( $this->object, $this->tablemock, $this->schemamock, $this->smmock, $this->connmock, $this->upmock );
77 | }
78 |
79 |
80 | public function testCall()
81 | {
82 | $this->schemamock->expects( $this->once() )->method( 'hasNamespace' );
83 | $this->object->hasNamespace( 'test' );
84 | }
85 |
86 |
87 | public function testCallMacro()
88 | {
89 | \Aimeos\Upscheme\Schema\DB::macro( 'unittest', function() { return 'yes'; } );
90 | $this->assertEquals( 'yes', $this->object->unittest() );
91 | }
92 |
93 |
94 | public function testClone()
95 | {
96 | $this->connmock->expects( $this->once() )->method( 'close' );
97 | $this->object->expects( $this->once() )->method( 'up' );
98 |
99 | $obj = clone $this->object;
100 | }
101 |
102 |
103 | public function testClose()
104 | {
105 | $this->connmock->expects( $this->once() )->method( 'close' );
106 | $this->object->close();
107 | }
108 |
109 |
110 | public function testDelete()
111 | {
112 | $this->connmock->expects( $this->once() )->method( 'delete' );
113 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->delete( 'unittest' ) );
114 | }
115 |
116 |
117 | public function testDropColumn()
118 | {
119 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
120 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
121 | $this->tablemock->expects( $this->once() )->method( 'dropColumn' );
122 |
123 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropColumn( 'unit', 'test' ) );
124 | }
125 |
126 |
127 | public function testDropColumnMultiple()
128 | {
129 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
130 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasColumn' )->willReturn( true );
131 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'dropColumn' );
132 |
133 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropColumn( 'unit', ['test', 'test2'] ) );
134 | }
135 |
136 |
137 | public function testDropForeign()
138 | {
139 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
140 | $this->tablemock->expects( $this->once() )->method( 'hasForeign' )->willReturn( true );
141 | $this->tablemock->expects( $this->once() )->method( 'dropForeign' );
142 |
143 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropForeign( 'unit', 'test' ) );
144 | }
145 |
146 |
147 | public function testDropForeignMultiple()
148 | {
149 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
150 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasForeign' )->willReturn( true );
151 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'dropForeign' );
152 |
153 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropForeign( 'unit', ['test', 'test2'] ) );
154 | }
155 |
156 |
157 | public function testDropIndex()
158 | {
159 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
160 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
161 | $this->tablemock->expects( $this->once() )->method( 'dropIndex' );
162 |
163 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropIndex( 'unit', 'test' ) );
164 | }
165 |
166 |
167 | public function testDropIndexMultiple()
168 | {
169 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
170 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasIndex' )->willReturn( true );
171 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'dropIndex' );
172 |
173 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropIndex( 'unit', ['test', 'test2'] ) );
174 | }
175 |
176 |
177 | public function testDropSequence()
178 | {
179 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( true );
180 | $this->schemamock->expects( $this->once() )->method( 'dropSequence' );
181 |
182 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropSequence( 'unit', 'test' ) );
183 | }
184 |
185 |
186 | public function testDropSequenceMultiple()
187 | {
188 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasSequence' )->willReturn( true );
189 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'dropSequence' );
190 |
191 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropSequence( ['test', 'test2'] ) );
192 | }
193 |
194 |
195 | public function testDropTable()
196 | {
197 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
198 |
199 | if( $this->object->type() !== 'oracle' ) {
200 | $this->schemamock->expects( $this->once() )->method( 'dropTable' );
201 | } else {
202 | $this->smmock->expects( $this->once() )->method( 'dropTable' );
203 | }
204 |
205 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropTable( 'unit', 'test' ) );
206 | }
207 |
208 |
209 | public function testDropTableMultiple()
210 | {
211 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
212 |
213 | if( $this->object->type() !== 'oracle' ) {
214 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'dropTable' );
215 | } else {
216 | $this->smmock->expects( $this->exactly( 2 ) )->method( 'dropTable' );
217 | }
218 |
219 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropTable( ['test', 'test2'] ) );
220 | }
221 |
222 |
223 | public function testDropView()
224 | {
225 | $this->object->expects( $this->once() )->method( 'getViews' )->willReturn( ['test' => new \stdClass] );
226 | $this->smmock->expects( $this->once() )->method( 'dropView' );
227 |
228 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropView( 'test' ) );
229 | }
230 |
231 |
232 | public function testDropViewMultiple()
233 | {
234 | $this->object->expects( $this->exactly( 2 ) )->method( 'getViews' )->willReturn( ['test' => new \stdClass, 'test2' => new \stdClass] );
235 | $this->smmock->expects( $this->exactly( 2 ) )->method( 'dropView' );
236 |
237 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->dropView( ['test', 'test2'] ) );
238 | }
239 |
240 |
241 | public function testExec()
242 | {
243 | $this->connmock->expects( $this->once() )->method( 'executeStatement' )->willReturn( 123 );
244 |
245 | $this->assertEquals( 123, $this->object->exec( 'test' ) );
246 | }
247 |
248 |
249 | public function testFor()
250 | {
251 | $this->connmock->expects( $this->once() )->method( 'executeStatement' )->willReturn( 123 );
252 |
253 | $this->assertEquals( 123, $this->object->exec( 'test' ) );
254 | }
255 |
256 |
257 | public function testForMultiple()
258 | {
259 | $this->connmock->expects( $this->exactly( 2 ) )->method( 'executeStatement' );
260 |
261 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->for( 'mysql', ['test', 'test2'] ) );
262 | }
263 |
264 |
265 | public function testForMismatch()
266 | {
267 | $this->connmock->expects( $this->never() )->method( 'executeStatement' );
268 |
269 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->for( 'postgresql', 'test' ) );
270 | }
271 |
272 |
273 | public function testHasColumn()
274 | {
275 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
276 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
277 |
278 | $this->assertTrue( $this->object->hasColumn( 'unit', 'test' ) );
279 | }
280 |
281 |
282 | public function testHasColumnNot()
283 | {
284 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
285 | $this->assertFalse( $this->object->hasColumn( 'unit', 'test' ) );
286 | }
287 |
288 |
289 | public function testHasForeign()
290 | {
291 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
292 | $this->tablemock->expects( $this->once() )->method( 'hasForeign' )->willReturn( true );
293 |
294 | $this->assertTrue( $this->object->hasForeign( 'unit', 'test' ) );
295 | }
296 |
297 |
298 | public function testHasForeignNot()
299 | {
300 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
301 | $this->assertFalse( $this->object->hasForeign( 'unit', 'test' ) );
302 | }
303 |
304 |
305 | public function testHasIndex()
306 | {
307 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
308 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
309 |
310 | $this->assertTrue( $this->object->hasIndex( 'unit', 'test' ) );
311 | }
312 |
313 |
314 | public function testHasIndexNot()
315 | {
316 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
317 | $this->assertFalse( $this->object->hasIndex( 'unit', 'test' ) );
318 | }
319 |
320 |
321 | public function testHasSequence()
322 | {
323 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( true );
324 | $this->assertTrue( $this->object->hasSequence( 'unit', 'test' ) );
325 | }
326 |
327 |
328 | public function testHasSequenceNot()
329 | {
330 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( false );
331 | $this->assertFalse( $this->object->hasSequence( 'unit', 'test' ) );
332 | }
333 |
334 |
335 | public function testHasTable()
336 | {
337 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
338 | $this->assertTrue( $this->object->hasTable( 'unit', 'test' ) );
339 | }
340 |
341 |
342 | public function testHasTableNot()
343 | {
344 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
345 | $this->assertFalse( $this->object->hasTable( 'unit', 'test' ) );
346 | }
347 |
348 |
349 | public function testHasView()
350 | {
351 | $this->object->expects( $this->once() )->method( 'getViews' )->willReturn( ['test' => new \stdClass] );
352 | $this->assertTrue( $this->object->hasView( 'test' ) );
353 | }
354 |
355 |
356 | public function testHasViewNot()
357 | {
358 | $this->object->expects( $this->once() )->method( 'getViews' )->willReturn( [] );
359 | $this->assertFalse( $this->object->hasView( 'test' ) );
360 | }
361 |
362 |
363 | public function testInsert()
364 | {
365 | $this->connmock->expects( $this->once() )->method( 'insert' );
366 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->insert( 'unittest', [] ) );
367 | }
368 |
369 |
370 | public function testLastId()
371 | {
372 | $this->connmock->expects( $this->once() )->method( 'lastInsertId' )->willReturn( '123' );
373 | $this->assertEquals( '123', $this->object->lastId() );
374 | }
375 |
376 |
377 | public function testName()
378 | {
379 | $this->schemamock->expects( $this->once() )->method( 'getName' )->willReturn( 'testdb' );
380 | $this->assertEquals( 'testdb', $this->object->name() );
381 | }
382 |
383 |
384 | public function testQ()
385 | {
386 | $this->connmock->expects( $this->once() )->method( 'quote' )->willReturn( '123' );
387 | $this->assertEquals( '123', $this->object->q( 123 ) );
388 | }
389 |
390 |
391 | public function testQi()
392 | {
393 | $this->connmock->expects( $this->once() )->method( 'quoteIdentifier' )->willReturn( '"key"' );
394 | $this->assertEquals( '"key"', $this->object->qi( 'key' ) );
395 | }
396 |
397 |
398 | public function testQuery()
399 | {
400 | $mock = $this->getMockBuilder( '\Doctrine\DBAL\Result' )
401 | ->disableOriginalConstructor()
402 | ->getMock();
403 |
404 | $this->connmock->expects( $this->once() )->method( 'executeQuery' )->willReturn( $mock );
405 |
406 | $this->assertInstanceOf( \Doctrine\DBAL\Result::class, $this->object->query( 'test' ) );
407 | }
408 |
409 |
410 | public function testRenameColumn()
411 | {
412 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
413 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
414 | $this->connmock->expects( $this->any() )->method( 'quoteIdentifier' )->willReturnArgument( 0 );
415 | $this->object->expects( $this->any() )->method( 'getColumnSQL' )->willReturn( 'test INTEGER' );
416 | $this->connmock->expects( $this->once() )->method( 'executeStatement' );
417 |
418 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->renameColumn( 'table', 'unit', 'test' ) );
419 | }
420 |
421 |
422 | public function testRenameColumnMultiple()
423 | {
424 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
425 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasColumn' )->willReturn( true );
426 | $this->connmock->expects( $this->any() )->method( 'quoteIdentifier' )->willReturnArgument( 0 );
427 | $this->object->expects( $this->any() )->method( 'getColumnSQL' )->willReturn( 'test INTEGER' );
428 | $this->connmock->expects( $this->exactly( 2 ) )->method( 'executeStatement' );
429 |
430 | $cols = ['unit' => 'test', 'unit2' => 'test2'];
431 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->renameColumn( 'table', $cols ) );
432 | }
433 |
434 |
435 | public function testRenameColumnException()
436 | {
437 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
438 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
439 |
440 | $this->expectException( \RuntimeException::class );
441 | $this->object->renameColumn( 'table', 'unit' );
442 | }
443 |
444 |
445 | public function testRenameIndex()
446 | {
447 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
448 | $this->object->expects( $this->once() )->method( 'table' )->willReturn( $this->tablemock );
449 | $this->tablemock->expects( $this->once() )->method( 'renameIndex' );
450 |
451 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->renameIndex( 'table', 'unit', 'test' ) );
452 | }
453 |
454 |
455 | public function testRenameTable()
456 | {
457 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
458 | $this->smmock->expects( $this->once() )->method( 'renameTable' );
459 |
460 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->renameTable( 'unit', 'test' ) );
461 | }
462 |
463 |
464 | public function testRenameTableMultiple()
465 | {
466 | $this->schemamock->expects( $this->exactly( 2 ) )->method( 'hasTable' )->willReturn( true );
467 | $this->smmock->expects( $this->exactly( 2 ) )->method( 'renameTable' );
468 |
469 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->renameTable( ['test', 'test2'] ) );
470 | }
471 |
472 |
473 | public function testRenameTableException()
474 | {
475 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
476 |
477 | $this->expectException( \RuntimeException::class );
478 | $this->object->renameTable( 'unit' );
479 | }
480 |
481 |
482 | public function testReset()
483 | {
484 | $this->smmock->expects( $this->once() )->method( 'introspectSchema' )
485 | ->willReturn( $this->schemamock );
486 |
487 | $this->object->reset();
488 | }
489 |
490 |
491 | public function testSequence()
492 | {
493 | $seqmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Sequence' )
494 | ->disableOriginalConstructor()
495 | ->getMock();
496 |
497 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( true );
498 | $this->schemamock->expects( $this->once() )->method( 'getSequence' )->willReturn( $seqmock );
499 |
500 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->sequence( 'unittest' ) );
501 | }
502 |
503 |
504 | public function testSequenceNew()
505 | {
506 | $seqmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Sequence' )
507 | ->disableOriginalConstructor()
508 | ->getMock();
509 |
510 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( false );
511 | $this->schemamock->expects( $this->once() )->method( 'createSequence' )->willReturn( $seqmock );
512 |
513 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->sequence( 'unittest' ) );
514 | }
515 |
516 |
517 | public function testSequenceClosure()
518 | {
519 | $seqmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Sequence' )
520 | ->disableOriginalConstructor()
521 | ->getMock();
522 |
523 | $this->schemamock->expects( $this->once() )->method( 'hasSequence' )->willReturn( false );
524 | $this->schemamock->expects( $this->once() )->method( 'createSequence' )->willReturn( $seqmock );
525 |
526 | $fcn = function( $seq ) {};
527 |
528 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Sequence::class, $this->object->sequence( 'unittest', $fcn ) );
529 | }
530 |
531 |
532 | public function testStmt()
533 | {
534 | $qbmock = $this->getMockBuilder( '\Doctrine\DBAL\Query\QueryBuilder' )
535 | ->disableOriginalConstructor()
536 | ->getMock();
537 |
538 | $this->connmock->expects( $this->once() )->method( 'createQueryBuilder' )->willReturn( $qbmock );
539 |
540 | $this->assertInstanceOf( \Doctrine\DBAL\Query\QueryBuilder::class, $this->object->stmt() );
541 | }
542 |
543 |
544 | public function testTable()
545 | {
546 | $tablemock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
547 | ->disableOriginalConstructor()
548 | ->getMock();
549 |
550 | $object = new \Aimeos\Upscheme\Schema\DB( $this->upmock, $this->connmock );
551 |
552 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
553 | $this->schemamock->expects( $this->once() )->method( 'getTable' )->willReturn( $tablemock );
554 |
555 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $object->table( 'unittest' ) );
556 | }
557 |
558 |
559 | public function testTableNew()
560 | {
561 | $tablemock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
562 | ->disableOriginalConstructor()
563 | ->getMock();
564 |
565 | $object = new \Aimeos\Upscheme\Schema\DB( $this->upmock, $this->connmock );
566 |
567 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
568 | $this->schemamock->expects( $this->once() )->method( 'createTable' )->willReturn( $tablemock );
569 |
570 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $object->table( 'unittest' ) );
571 | }
572 |
573 |
574 | public function testTableClosure()
575 | {
576 | $tablemock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
577 | ->disableOriginalConstructor()
578 | ->getMock();
579 |
580 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
581 | ->setConstructorArgs( [$this->upmock, $this->connmock] )
582 | ->onlyMethods( ['up'] )
583 | ->getMock();
584 |
585 | $this->schemamock->expects( $this->once() )->method( 'hasTable' )->willReturn( false );
586 | $this->schemamock->expects( $this->once() )->method( 'createTable' )->willReturn( $tablemock );
587 |
588 | $fcn = function( $seq ) {};
589 |
590 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $object->table( 'unittest', $fcn ) );
591 | }
592 |
593 |
594 | public function testTransaction()
595 | {
596 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
597 | ->setConstructorArgs( [$this->upmock, $this->connmock] )
598 | ->onlyMethods( ['up'] )
599 | ->getMock();
600 |
601 | $fcn = function( $db ) {};
602 |
603 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $object->transaction( $fcn ) );
604 | }
605 |
606 |
607 | public function testTransactionException()
608 | {
609 | $object = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
610 | ->setConstructorArgs( [$this->upmock, $this->connmock] )
611 | ->onlyMethods( ['up'] )
612 | ->getMock();
613 |
614 | $fcn = function( $db ) {
615 | throw new \Exception();
616 | };
617 |
618 | $this->expectException( \Exception::class );
619 | $object->transaction( $fcn );
620 | }
621 |
622 |
623 | public function testType()
624 | {
625 | $this->assertEquals( 'mysql', $this->object->type() );
626 | }
627 |
628 |
629 | public function testUpdate()
630 | {
631 | $this->connmock->expects( $this->once() )->method( 'update' );
632 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $this->object->update( 'unittest', [] ) );
633 | }
634 |
635 |
636 | public function testView()
637 | {
638 | $view = new class {
639 | public function getNamespaceName() { return ''; }
640 | public function getShortestName() { return 'unittest'; }
641 | };
642 |
643 | $object = new \Aimeos\Upscheme\Schema\DB( $this->upmock, $this->connmock );
644 |
645 | $this->smmock->expects( $this->once() )->method( 'listViews' )->willReturn( [$view] );
646 |
647 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $object->view( 'unittest', 'CREATE VIEW' ) );
648 | }
649 |
650 |
651 | public function testViewNew()
652 | {
653 | $object = new \Aimeos\Upscheme\Schema\DB( $this->upmock, $this->connmock );
654 |
655 | $this->smmock->expects( $this->once() )->method( 'listViews' )->willReturn( [] );
656 | $this->smmock->expects( $this->once() )->method( 'createView' );
657 |
658 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\DB::class, $object->view( 'unittest', 'CREATE VIEW' ) );
659 | }
660 | }
661 |
--------------------------------------------------------------------------------
/src/Schema/Table.php:
--------------------------------------------------------------------------------
1 | db = $db;
44 | $this->table = $table;
45 | }
46 |
47 |
48 | /**
49 | * Calls custom methods or passes unknown method calls to the Doctrine table object
50 | *
51 | * @param string $method Name of the method
52 | * @param array $args Method parameters
53 | * @return mixed Return value of the called method
54 | */
55 | public function __call( string $method, array $args )
56 | {
57 | if( self::macro( $method ) ) {
58 | return $this->call( $method, ...$args );
59 | }
60 |
61 | return $this->table->{$method}( ...$args );
62 | }
63 |
64 |
65 | /**
66 | * Returns the value for the given table option
67 | *
68 | * @param string $name Table option name
69 | * @return mixed Table option value
70 | */
71 | public function __get( string $name )
72 | {
73 | return $this->opt( $name );
74 | }
75 |
76 |
77 | /**
78 | * Sets the new value for the given table option
79 | *
80 | * @param string $name Table option name
81 | * @param mixed $value Table option value
82 | */
83 | public function __set( string $name, $value )
84 | {
85 | $this->opt( $name, $value );
86 | }
87 |
88 |
89 | /**
90 | * Creates a new column of type "bigint" or returns the existing one
91 | *
92 | * @param string $name Name of the column
93 | * @return \Aimeos\Upscheme\Schema\Column Column object
94 | */
95 | public function bigint( string $name ) : Column
96 | {
97 | return $this->col( $name, 'bigint' );
98 | }
99 |
100 |
101 | /**
102 | * Creates a new column of type "binary" or returns the existing one
103 | *
104 | * @param string $name Name of the column
105 | * @param int $length Length of the column in bytes
106 | * @return \Aimeos\Upscheme\Schema\Column Column object
107 | */
108 | public function binary( string $name, int $length = 255 ) : Column
109 | {
110 | return $this->col( $name, 'binary' )->length( $length );
111 | }
112 |
113 |
114 | /**
115 | * Creates a new column of type "blob" or returns the existing one
116 | *
117 | * The maximum length of a "blob" column is 2GB.
118 | *
119 | * @param string $name Name of the column
120 | * @param int $length Length of the column in bytes
121 | * @return \Aimeos\Upscheme\Schema\Column Column object
122 | */
123 | public function blob( string $name, int $length = 0x7fff ) : Column
124 | {
125 | return $this->col( $name, 'blob' )->length( $length );
126 | }
127 |
128 |
129 | /**
130 | * Creates a new column of type "boolean" or returns the existing one
131 | *
132 | * This method is an alias for boolean()
133 | *
134 | * @param string $name Name of the column
135 | * @return \Aimeos\Upscheme\Schema\Column Column object
136 | */
137 | public function bool( string $name ) : Column
138 | {
139 | return $this->boolean( $name );
140 | }
141 |
142 |
143 | /**
144 | * Creates a new column of type "boolean" or returns the existing one
145 | *
146 | * @param string $name Name of the column
147 | * @return \Aimeos\Upscheme\Schema\Column Column object
148 | */
149 | public function boolean( string $name ) : Column
150 | {
151 | return $this->col( $name, 'boolean' )->default( false );
152 | }
153 |
154 |
155 | /**
156 | * Creates a new column of type "char" with a fixed length or returns the existing one
157 | *
158 | * @param string $name Name of the column
159 | * @param int $length Length of the column in characters
160 | * @return \Aimeos\Upscheme\Schema\Column Column object
161 | */
162 | public function char( string $name, int $length ) : Column
163 | {
164 | return $this->col( $name, 'string' )->length( $length )->fixed( true );
165 | }
166 |
167 |
168 | /**
169 | * Creates a new column or returns the existing one
170 | *
171 | * If the column doesn't exist yet, it will be created.
172 | *
173 | * @param string $name Name of the column
174 | * @param string $type|null Type of the column
175 | * @return \Aimeos\Upscheme\Schema\Column Column object
176 | */
177 | public function col( string $name, ?string $type = null ) : Column
178 | {
179 | if( $this->table->hasColumn( $name ) ) {
180 | $col = $this->table->getColumn( $name );
181 | } else {
182 | $col = $this->table->addColumn( $this->db->qi( $name ), $type ?: 'string' );
183 | }
184 |
185 | if( $type ) {
186 | $col->setType( \Doctrine\DBAL\Types\Type::getType( $type ) );
187 | }
188 |
189 | return new Column( $this->db, $this, $col );
190 | }
191 |
192 |
193 | /**
194 | * Creates a new column of type "date" or returns the existing one
195 | *
196 | * @param string $name Name of the column
197 | * @return \Aimeos\Upscheme\Schema\Column Column object
198 | */
199 | public function date( string $name ) : Column
200 | {
201 | return $this->col( $name, 'date' );
202 | }
203 |
204 |
205 | /**
206 | * Creates a new column of type "datetime" or returns the existing one
207 | *
208 | * @param string $name Name of the column
209 | * @return \Aimeos\Upscheme\Schema\Column Column object
210 | */
211 | public function datetime( string $name ) : Column
212 | {
213 | return $this->col( $name, 'datetime' );
214 | }
215 |
216 |
217 | /**
218 | * Creates a new column of type "datetimetz" or returns the existing one
219 | *
220 | * @param string $name Name of the column
221 | * @return \Aimeos\Upscheme\Schema\Column Column object
222 | */
223 | public function datetimetz( string $name ) : Column
224 | {
225 | return $this->col( $name, 'datetimetz' );
226 | }
227 |
228 |
229 | /**
230 | * Creates a new column of type "decimal" or returns the existing one
231 | *
232 | * @param string $name Name of the column
233 | * @param int $digits Total number of decimal digits including decimals
234 | * @param int $decimals Number of digits after the decimal point
235 | * @return \Aimeos\Upscheme\Schema\Column Column object
236 | */
237 | public function decimal( string $name, int $digits, int $decimals = 2 ) : Column
238 | {
239 | return $this->col( $name, 'decimal' )->precision( $digits )->scale( $decimals )->default( 0 );
240 | }
241 |
242 |
243 | /**
244 | * Creates a new column of type "float" or returns the existing one
245 | *
246 | * @param string $name Name of the column
247 | * @return \Aimeos\Upscheme\Schema\Column Column object
248 | */
249 | public function float( string $name ) : Column
250 | {
251 | return $this->col( $name, 'float' )->default( 0 );
252 | }
253 |
254 |
255 | /**
256 | * Creates a new column of type "guid" or returns the existing one
257 | *
258 | * @param string $name Name of the column
259 | * @return \Aimeos\Upscheme\Schema\Column Column object
260 | */
261 | public function guid( string $name ) : Column
262 | {
263 | return $this->col( $name, 'guid' );
264 | }
265 |
266 |
267 | /**
268 | * Creates a new column of type "integer" or returns the existing one
269 | *
270 | * This method is an alias for integer()
271 | *
272 | * @param string $name Name of the column
273 | * @return \Aimeos\Upscheme\Schema\Column Column object
274 | */
275 | public function int( string $name ) : Column
276 | {
277 | return $this->integer( $name );
278 | }
279 |
280 |
281 | /**
282 | * Creates a new column of type "integer" or returns the existing one
283 | *
284 | * @param string $name Name of the column
285 | * @return \Aimeos\Upscheme\Schema\Column Column object
286 | */
287 | public function integer( string $name ) : Column
288 | {
289 | return $this->col( $name, 'integer' );
290 | }
291 |
292 |
293 | /**
294 | * Creates a new column of type "json" or returns the existing one
295 | *
296 | * @param string $name Name of the column
297 | * @return \Aimeos\Upscheme\Schema\Column Column object
298 | */
299 | public function json( string $name ) : Column
300 | {
301 | return $this->col( $name, 'json' );
302 | }
303 |
304 |
305 | /**
306 | * Creates a new column of type "smallint" or returns the existing one
307 | *
308 | * @param string $name Name of the column
309 | * @return \Aimeos\Upscheme\Schema\Column Column object
310 | */
311 | public function smallint( string $name ) : Column
312 | {
313 | return $this->col( $name, 'smallint' )->default( 0 );
314 | }
315 |
316 |
317 | /**
318 | * Creates a new column of type "string" or returns the existing one
319 | *
320 | * This type should be used for up to 255 characters. For more characters,
321 | * use the "text" type.
322 | *
323 | * @param string $name Name of the column
324 | * @param int $length Length of the column in characters
325 | * @return \Aimeos\Upscheme\Schema\Column Column object
326 | */
327 | public function string( string $name, int $length = 255 ) : Column
328 | {
329 | return $this->col( $name, 'string' )->length( $length );
330 | }
331 |
332 |
333 | /**
334 | * Creates a new column of type "text" or returns the existing one
335 | *
336 | * The maximum length of a "text" column is 2GB.
337 | *
338 | * @param string $name Name of the column
339 | * @param int $length Length of the column in characters
340 | * @return \Aimeos\Upscheme\Schema\Column Column object
341 | */
342 | public function text( string $name, int $length = 0xffff ) : Column
343 | {
344 | return $this->col( $name, 'text' )->length( $length );
345 | }
346 |
347 |
348 | /**
349 | * Creates a new column of type "time" or returns the existing one
350 | *
351 | * This datatype is not available when using Oracle databases
352 | *
353 | * @param string $name Name of the column
354 | * @return \Aimeos\Upscheme\Schema\Column Column object
355 | */
356 | public function time( string $name ) : Column
357 | {
358 | return $this->col( $name, 'time' );
359 | }
360 |
361 |
362 | /**
363 | * Creates a new column of type "guid" or returns the existing one
364 | *
365 | * This method is an alias for guid()
366 | *
367 | * @param string $name Name of the column
368 | * @return \Aimeos\Upscheme\Schema\Column Column object
369 | */
370 | public function uuid( string $name ) : Column
371 | {
372 | return $this->guid( $name );
373 | }
374 |
375 |
376 | /**
377 | * Drops the column given by its name if it exists
378 | *
379 | * @param array|string $name Name of the column or columns
380 | * @return self Same object for fluid method calls
381 | */
382 | public function dropColumn( $name ) : self
383 | {
384 | foreach( (array) $name as $entry )
385 | {
386 | if( $this->table->hasColumn( $entry ) ) {
387 | $this->table->dropColumn( $entry );
388 | }
389 | }
390 |
391 | return $this;
392 | }
393 |
394 |
395 | /**
396 | * Drops the index given by its name if it exists
397 | *
398 | * @param array|string $name Name of the index or indexes
399 | * @return self Same object for fluid method calls
400 | */
401 | public function dropIndex( $name ) : self
402 | {
403 | foreach( (array) $name as $entry )
404 | {
405 | if( $this->table->hasIndex( $entry ) ) {
406 | $this->table->dropIndex( $entry );
407 | }
408 | }
409 |
410 | return $this;
411 | }
412 |
413 |
414 | /**
415 | * Drops the foreign key constraint given by its name if it exists
416 | *
417 | * @param array|string $name Name of the foreign key constraint or constraints
418 | * @return self Same object for fluid method calls
419 | */
420 | public function dropForeign( $name ) : self
421 | {
422 | foreach( (array) $name as $entry )
423 | {
424 | if( $this->table->hasForeignKey( $entry ) ) {
425 | $this->table->removeForeignKey( $entry );
426 | }
427 | }
428 |
429 | return $this;
430 | }
431 |
432 |
433 | /**
434 | * Drops the primary key if it exists
435 | *
436 | * @return self Same object for fluid method calls
437 | */
438 | public function dropPrimary() : self
439 | {
440 | if( $this->table->getPrimaryKey() ) {
441 | $this->table->dropPrimaryKey();
442 | }
443 |
444 | return $this;
445 | }
446 |
447 |
448 | /**
449 | * Checks if the column exists
450 | *
451 | * @param array|string $name Name of the column or columns
452 | * @return bool TRUE if the columns exists, FALSE if not
453 | */
454 | public function hasColumn( $name ) : bool
455 | {
456 | foreach( (array) $name as $entry )
457 | {
458 | if( !$this->table->hasColumn( $entry ) ) {
459 | return false;
460 | }
461 | }
462 |
463 | return true;
464 | }
465 |
466 |
467 | /**
468 | * Checks if the index exists
469 | *
470 | * @param array|string $name Name of the index or indexes
471 | * @return bool TRUE if the indexes exists, FALSE if not
472 | */
473 | public function hasIndex( $name ) : bool
474 | {
475 | foreach( (array) $name as $entry )
476 | {
477 | if( !$this->table->hasIndex( $entry ) ) {
478 | return false;
479 | }
480 | }
481 |
482 | return true;
483 | }
484 |
485 |
486 | /**
487 | * Checks if the foreign key constraint exists
488 | *
489 | * @param array|string $name Name of the foreign key constraint or constraints
490 | * @return bool TRUE if the foreign key constraints exists, FALSE if not
491 | */
492 | public function hasForeign( $name ) : bool
493 | {
494 | foreach( (array) $name as $entry )
495 | {
496 | if( !$this->table->hasForeignKey( $entry ) ) {
497 | return false;
498 | }
499 | }
500 |
501 | return true;
502 | }
503 |
504 |
505 | /**
506 | * Creates a new foreign key or returns the existing one
507 | *
508 | * @param array|string $localcolumn Name of the local column or columns
509 | * @param string $foreigntable Name of the referenced table
510 | * @param array|string $foreigncolumn Name of the referenced column or columns
511 | * @param string|null $name Name of the foreign key constraint and foreign key index or NULL for autogenerated name
512 | * @return \Aimeos\Upscheme\Schema\Foreign Foreign key constraint object
513 | */
514 | public function foreign( $localcolumn, string $foreigntable, $foreigncolumn = 'id', ?string $name = null ) : Foreign
515 | {
516 | $localcolumn = (array) $localcolumn;
517 | $foreigncolumn = (array) $foreigncolumn;
518 |
519 | if( !$this->db->hasTable( $foreigntable ) ) {
520 | throw new \RuntimeException( sprintf( 'Table "%1$s" is missing', $foreigntable ) );
521 | }
522 |
523 | $table = $this->db->table( $foreigntable );
524 |
525 | foreach( $foreigncolumn as $idx => $col )
526 | {
527 | if( !$table->hasColumn( $col ) ) {
528 | throw new \RuntimeException( sprintf( 'Column "%1$s" in table "%2$s" is missing', $col, $table->name() ) );
529 | }
530 |
531 | if( !isset( $localcolumn[$idx] ) ) {
532 | throw new \LogicException( sprintf( 'No matching local column for foreign column "%1$s" in table "%2$s"', $col, $foreigntable ) );
533 | }
534 |
535 | $this->copyColumn( $table->getColumn( $col ), $localcolumn[$idx] );
536 | }
537 |
538 | $name = $name ?: $this->nameIndex( $this->name(), $localcolumn, 'fk' );
539 | return new Foreign( $this->db, $this, $this->table, $localcolumn, $foreigntable, $foreigncolumn, $name );
540 | }
541 |
542 |
543 | /**
544 | * Creates a new index or replaces an existing one
545 | *
546 | * @param array|string $columns Name of the columns or columns spawning the index
547 | * @param string|null $name Index name or NULL for autogenerated name
548 | * @param array $flags Name of DB-specific index flags
549 | * @param array $options Associative key/value pairs of DB-specific index options
550 | * @return self Same object for fluid method calls
551 | */
552 | public function index( $columns, ?string $name = null, array $flags = [], array $options = [] ) : self
553 | {
554 | $columns = (array) $columns;
555 | $name = $name ?: $this->nameIndex( $this->name(), $columns, 'idx' );
556 |
557 | if( $name && $this->table->hasIndex( $name ) )
558 | {
559 | if( $this->table->getIndex( $name )->spansColumns( $columns ) ) {
560 | return $this;
561 | }
562 |
563 | $this->table->dropIndex( $name );
564 | }
565 |
566 | if( $name === null )
567 | {
568 | foreach( $this->table->getIndexes() as $index )
569 | {
570 | if( $index->spansColumns( $columns ) ) {
571 | return $this;
572 | }
573 | }
574 | }
575 |
576 | foreach( $columns as $key => $column ) {
577 | $columns[$key] = $this->db->qi( $column );
578 | }
579 |
580 | $this->table->addIndex( $columns, $name ? $this->db->qi( $name ) : null, $flags, $options );
581 | return $this;
582 | }
583 |
584 |
585 | /**
586 | * Returns the name of the table
587 | *
588 | * @return string Table name
589 | */
590 | public function name() : string
591 | {
592 | return $this->table->getName();
593 | }
594 |
595 |
596 | /**
597 | * Sets a custom schema option or returns the current value
598 | *
599 | * Available custom schema options are:
600 | * - charset (MySQL)
601 | * - collation (MySQL)
602 | * - engine (MySQL)
603 | * - temporary (MySQL)
604 | *
605 | * @param string $name Name of the table-related custom schema option
606 | * @param mixed $value Value of the custom schema option
607 | * @return self|mixed Same object for setting value, current value without second parameter
608 | */
609 | public function opt( string $name, $value = null )
610 | {
611 | if( $value === null ) {
612 | return $this->table->hasOption( $name ) ? $this->table->getOption( $name ) : null;
613 | }
614 |
615 | $this->table->addOption( $name, $value );
616 | return $this;
617 | }
618 |
619 |
620 | /**
621 | * Creates a new primary index or replaces an existing one
622 | *
623 | * @param array|string $columns Name of the columns or columns spawning the index
624 | * @param string|null $name Index name or NULL for autogenerated name
625 | * @return self Same object for fluid method calls
626 | */
627 | public function primary( $columns, ?string $name = null ) : self
628 | {
629 | $columns = (array) $columns;
630 | $index = $this->table->getPrimaryKey();
631 |
632 | if( $index && $index->spansColumns( $columns ) ) {
633 | return $this;
634 | }
635 |
636 | if( $index ) {
637 | $this->table->dropPrimaryKey();
638 | }
639 |
640 | foreach( $columns as $key => $column ) {
641 | $columns[$key] = $this->db->qi( $column );
642 | }
643 |
644 | $name = $name ?: $this->nameIndex( $this->name(), $columns, 'pk' );
645 | $this->table->setPrimaryKey( $columns, $name ? $this->db->qi( $name ) : 'primary' );
646 | return $this;
647 | }
648 |
649 |
650 | /**
651 | * Renames a column or a list of column
652 | *
653 | * @param array|string $from Column name or array of old/new column names
654 | * @param string|null $to New column name or NULL if first parameter is an array
655 | * @return self Same object for fluid method calls
656 | */
657 | public function renameColumn( $from, ?string $to = null ) : self
658 | {
659 | if( !$this->hasColumn( $from ) ) {
660 | return $this;
661 | }
662 |
663 | $this->db->renameColumn( $this->name(), $from, $to );
664 | return $this;
665 | }
666 |
667 |
668 | /**
669 | * Renames an index or a list of indexes
670 | *
671 | * @param array|string $from Index name or array of old/new index names (if new index name is NULL, it will be generated)
672 | * @param string|null $to New index name or NULL for autogenerated name (ignored if first parameter is an array)
673 | * @return self Same object for fluid method calls
674 | */
675 | public function renameIndex( $from, ?string $to = null ) : self
676 | {
677 | if( !is_array( $from ) ) {
678 | $from = [$from => $to];
679 | }
680 |
681 | foreach( $from as $name => $to )
682 | {
683 | if( $this->table->hasIndex( $name ) )
684 | {
685 | if( !$to )
686 | {
687 | $index = $this->table->getIndex( $name );
688 | $type = $index->isPrimary() ? 'pk' : ( $index->isUnique() ? 'unq' : 'idx' );
689 | $to = $this->nameIndex( $this->name(), $index->getColumns(), $type );
690 | }
691 |
692 | $this->table->renameIndex( $this->db->qi( $name ), $to ? $this->db->qi( $to ) : null );
693 | }
694 | }
695 |
696 | return $this->up();
697 | }
698 |
699 |
700 | /**
701 | * Creates a new spatial index or replaces an existing one
702 | *
703 | * @param array|string $columns Name of the columns or columns spawning the index
704 | * @param string|null $name Index name or NULL for autogenerated name
705 | * @return self Same object for fluid method calls
706 | */
707 | public function spatial( $columns, ?string $name = null ) : self
708 | {
709 | $columns = (array) $columns;
710 | $name = $name ?: $this->nameIndex( $this->name(), $columns, 'idx' );
711 |
712 | if( $name && $this->table->hasIndex( $name ) )
713 | {
714 | $index = $this->table->getIndex( $name );
715 |
716 | if( $index->hasFlag( 'spatial' ) && $index->spansColumns( $columns ) ) {
717 | return $this;
718 | }
719 |
720 | $this->table->dropIndex( $name );
721 | }
722 |
723 | foreach( $columns as $key => $column ) {
724 | $columns[$key] = $this->db->qi( $column );
725 | }
726 |
727 | $this->table->addIndex( $columns, $name, ['spatial' => true] );
728 | return $this;
729 | }
730 |
731 |
732 | /**
733 | * Creates a new unique index or replaces an existing one
734 | *
735 | * @param array|string $columns Name of the columns or columns spawning the index
736 | * @param string|null $name Index name or NULL for autogenerated name
737 | * @return self Same object for fluid method calls
738 | */
739 | public function unique( $columns, ?string $name = null ) : self
740 | {
741 | $columns = (array) $columns;
742 | $name = $name ?: $this->nameIndex( $this->name(), $columns, 'unq' );
743 |
744 | if( $name && $this->table->hasIndex( $name ) )
745 | {
746 | $index = $this->table->getIndex( $name );
747 |
748 | if( $index->isUnique() && $index->spansColumns( $columns ) ) {
749 | return $this;
750 | }
751 |
752 | $this->table->dropIndex( $name );
753 | }
754 |
755 | foreach( $columns as $key => $column ) {
756 | $columns[$key] = $this->db->qi( $column );
757 | }
758 |
759 | $this->table->addUniqueIndex( $columns, $name ? $this->db->qi( $name ) : null );
760 | return $this;
761 | }
762 |
763 |
764 | /**
765 | * Applies the changes to the database schema
766 | *
767 | * @return self Same object for fluid method calls
768 | */
769 | public function up() : self
770 | {
771 | $this->db->up();
772 | return $this;
773 | }
774 |
775 |
776 | /**
777 | * Adds a new column to the table by copying the given column
778 | *
779 | * If the column already exists, the column specification is changed if necessary
780 | *
781 | * @param \Doctrine\DBAL\Schema\Column $column Doctrine column object
782 | * @param string $name New local column name
783 | */
784 | protected function copyColumn( \Doctrine\DBAL\Schema\Column $column, $name ) : void
785 | {
786 | $options = $column->toArray();
787 | $custom = array_intersect_key( $options, ['charset' => null, 'collation' => null, 'check' => null] );
788 | unset( $options['name'], $options['autoincrement'], $options['charset'], $options['collation'], $options['check'] );
789 |
790 | if( $this->table->hasColumn( $name ) ) {
791 | $this->table->modifyColumn( $this->db->qi( $name ), $options + ['platformOptions' => $custom] );
792 | } else {
793 | $types = $column->getType()->getTypeRegistry();
794 | $this->table->addColumn( $this->db->qi( $name ), $types->lookupName( $column->getType() ), $options + ['platformOptions' => $custom] );
795 | }
796 | }
797 |
798 |
799 | /**
800 | * Returns the name that should be used for the index
801 | *
802 | * Available types are:
803 | * - idx: Regular and spatial indexes
804 | * - fk: Foreign key index
805 | * - pk: Primary key index
806 | * - unq: Unique index
807 | *
808 | * @param string $table Table name
809 | * @param array $columns Column names
810 | * @param string $type Index type
811 | * @return string|null Name of the index or NULL to use the generated name by Doctrine DBAL
812 | */
813 | protected function nameIndex( string $table, array $columns, string $type = 'idx' ) : ?string
814 | {
815 | if( $fcn = self::macro( 'nameIndex' ) ) {
816 | return $fcn( $table, $columns, $type );
817 | }
818 |
819 | return null;
820 | }
821 | }
--------------------------------------------------------------------------------
/tests/Upscheme/Schema/TableTest.php:
--------------------------------------------------------------------------------
1 | dbmock = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\DB' )
21 | ->disableOriginalConstructor()
22 | ->getMock();
23 |
24 | $this->dbmock->expects( $this->any() )->method( 'qi' )
25 | ->willReturnCallback( function( $value ) {
26 | return '"' . $value . '"';
27 | } );
28 |
29 | $methods = [
30 | 'addIndex', 'getIndex', 'getIndexes', 'hasIndex', 'renameIndex', 'dropIndex',
31 | 'dropPrimaryKey', 'getPrimaryKey', 'setPrimaryKey',
32 | 'addUniqueIndex', 'hasUniqueConstraint', 'removeUniqueConstraint',
33 | 'getName', 'addOption', 'getOption', 'hasOption',
34 | 'dropColumn', 'hasColumn',
35 | 'hasForeignKey', 'removeForeignKey',
36 | ];
37 |
38 | $this->tablemock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
39 | ->disableOriginalConstructor()
40 | ->onlyMethods( $methods )
41 | ->getMock();
42 |
43 | $this->object = new \Aimeos\Upscheme\Schema\Table( $this->dbmock, $this->tablemock );
44 | }
45 |
46 |
47 | protected function tearDown() : void
48 | {
49 | unset( $this->object, $this->tablemock, $this->dbmock );
50 | }
51 |
52 |
53 | public function testCall()
54 | {
55 | $this->tablemock->expects( $this->once() )->method( 'hasForeignKey' );
56 |
57 | $this->object->hasForeignKey( 'unittest' );
58 | }
59 |
60 |
61 | public function testCallMacro()
62 | {
63 | \Aimeos\Upscheme\Schema\Table::macro( 'unittest', function() { return 'yes'; } );
64 |
65 | $this->assertEquals( 'yes', $this->object->unittest() );
66 | }
67 |
68 |
69 | public function testGetMagic()
70 | {
71 | $this->tablemock->expects( $this->once() )->method( 'hasOption' )
72 | ->willReturn( true );
73 |
74 | $this->tablemock->expects( $this->once() )->method( 'getOption' )
75 | ->willReturn( 'yes' );
76 |
77 | $this->assertEquals( 'yes', $this->object->unittest );
78 | }
79 |
80 |
81 | public function testSetMagic()
82 | {
83 | $this->tablemock->expects( $this->once() )->method( 'addOption' );
84 |
85 | $this->object->unittest = 'yes';
86 | }
87 |
88 |
89 | public function testOptGet()
90 | {
91 | $this->tablemock->expects( $this->once() )->method( 'hasOption' )
92 | ->willReturn( true );
93 |
94 | $this->tablemock->expects( $this->once() )->method( 'getOption' )
95 | ->willReturn( 'yes' );
96 |
97 | $this->assertEquals( 'yes', $this->object->opt( 'unittest' ) );
98 | }
99 |
100 |
101 | public function testOptSet()
102 | {
103 | $this->tablemock->expects( $this->once() )->method( 'addOption' );
104 |
105 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->opt( 'unittest', 'yes' ) );
106 | }
107 |
108 |
109 | public function testBigid()
110 | {
111 | \Aimeos\Upscheme\Schema\Table::macro( 'bigid', function( string $name = null ) : Column {
112 | return $this->bigint( $name ?: 'id' )->seq( true )->primary();
113 | } );
114 |
115 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
116 |
117 | $col = $this->object->bigid();
118 |
119 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
120 | $this->assertEquals( 'id', $col->name() );
121 | $this->assertEquals( 'bigint', $col->type() );
122 | $this->assertTrue( $col->seq() );
123 | }
124 |
125 |
126 | public function testBigidName()
127 | {
128 | \Aimeos\Upscheme\Schema\Table::macro( 'bigid', function( string $name = null ) : Column {
129 | return $this->bigint( $name ?: 'id' )->seq( true )->primary();
130 | } );
131 |
132 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
133 |
134 | $col = $this->object->bigid( 'uid' );
135 |
136 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
137 | $this->assertEquals( 'uid', $col->name() );
138 | $this->assertEquals( 'bigint', $col->type() );
139 | $this->assertTrue( $col->seq() );
140 | }
141 |
142 |
143 | public function testBigint()
144 | {
145 | $col = $this->object->bigint( 'unittest' );
146 |
147 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
148 | $this->assertEquals( 'unittest', $col->name() );
149 | $this->assertEquals( 'bigint', $col->type() );
150 | $this->assertEquals( 0, $col->default() );
151 | }
152 |
153 |
154 | public function testBinary()
155 | {
156 | $col = $this->object->binary( 'unittest', 255 );
157 |
158 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
159 | $this->assertEquals( 'unittest', $col->name() );
160 | $this->assertEquals( 'binary', $col->type() );
161 | $this->assertEquals( 255, $col->length() );
162 | $this->assertEquals( '', $col->default() );
163 | }
164 |
165 |
166 | public function testBlob()
167 | {
168 | $col = $this->object->blob( 'unittest', 0x7fff );
169 |
170 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
171 | $this->assertEquals( 'unittest', $col->name() );
172 | $this->assertEquals( 'blob', $col->type() );
173 | $this->assertEquals( 0x7fff, $col->length() );
174 | $this->assertEquals( '', $col->default() );
175 | }
176 |
177 |
178 | public function testBool()
179 | {
180 | $col = $this->object->bool( 'unittest' );
181 |
182 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
183 | $this->assertEquals( 'unittest', $col->name() );
184 | $this->assertEquals( 'boolean', $col->type() );
185 | $this->assertEquals( false, $col->default() );
186 | }
187 |
188 |
189 | public function testBoolean()
190 | {
191 | $col = $this->object->boolean( 'unittest' );
192 |
193 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
194 | $this->assertEquals( 'unittest', $col->name() );
195 | $this->assertEquals( 'boolean', $col->type() );
196 | $this->assertEquals( false, $col->default() );
197 | }
198 |
199 |
200 | public function testChar()
201 | {
202 | $col = $this->object->char( 'unittest', 3 );
203 |
204 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
205 | $this->assertEquals( 'unittest', $col->name() );
206 | $this->assertEquals( 'string', $col->type() );
207 | $this->assertEquals( true, $col->fixed() );
208 | $this->assertEquals( 3, $col->length() );
209 | }
210 |
211 |
212 | public function testCol()
213 | {
214 | $col = $this->object->col( 'unittest', 'integer' );
215 |
216 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
217 | $this->assertEquals( 'unittest', $col->name() );
218 | $this->assertEquals( 'integer', $col->type() );
219 |
220 |
221 | $this->tablemock->expects( $this->any() )->method( 'hasColumn' )->willReturn( true );
222 |
223 | $col = $this->object->col( 'unittest' );
224 |
225 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
226 | $this->assertEquals( 'unittest', $col->name() );
227 | $this->assertEquals( 'integer', $col->type() );
228 |
229 |
230 | $this->tablemock->expects( $this->any() )->method( 'hasColumn' )->willReturn( true );
231 |
232 | $col = $this->object->col( 'unittest', 'bigint' );
233 |
234 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
235 | $this->assertEquals( 'unittest', $col->name() );
236 | $this->assertEquals( 'bigint', $col->type() );
237 | }
238 |
239 |
240 | public function testDate()
241 | {
242 | $col = $this->object->date( 'unittest' );
243 |
244 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
245 | $this->assertEquals( 'unittest', $col->name() );
246 | $this->assertEquals( 'date', $col->type() );
247 | }
248 |
249 |
250 | public function testDatetime()
251 | {
252 | $col = $this->object->datetime( 'unittest' );
253 |
254 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
255 | $this->assertEquals( 'unittest', $col->name() );
256 | $this->assertEquals( 'datetime', $col->type() );
257 | }
258 |
259 |
260 | public function testDatetimetz()
261 | {
262 | $col = $this->object->datetimetz( 'unittest' );
263 |
264 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
265 | $this->assertEquals( 'unittest', $col->name() );
266 | $this->assertEquals( 'datetimetz', $col->type() );
267 | }
268 |
269 |
270 | public function testDecimal()
271 | {
272 | $col = $this->object->decimal( 'unittest', 10, 3 );
273 |
274 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
275 | $this->assertEquals( 'unittest', $col->name() );
276 | $this->assertEquals( 'decimal', $col->type() );
277 | $this->assertEquals( 10, $col->precision() );
278 | $this->assertEquals( 3, $col->scale() );
279 | $this->assertEquals( 0, $col->default() );
280 | }
281 |
282 |
283 | public function testFloat()
284 | {
285 | $col = $this->object->float( 'unittest' );
286 |
287 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
288 | $this->assertEquals( 'unittest', $col->name() );
289 | $this->assertEquals( 'float', $col->type() );
290 | $this->assertEquals( 0, $col->default() );
291 | }
292 |
293 |
294 | public function testGuid()
295 | {
296 | $col = $this->object->guid( 'unittest' );
297 |
298 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
299 | $this->assertEquals( 'unittest', $col->name() );
300 | $this->assertEquals( 'guid', $col->type() );
301 | }
302 |
303 |
304 | public function testId()
305 | {
306 | \Aimeos\Upscheme\Schema\Table::macro( 'id', function( string $name = null ) : Column {
307 | return $this->integer( $name ?: 'id' )->seq( true )->primary();
308 | } );
309 |
310 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
311 |
312 | $col = $this->object->id();
313 |
314 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
315 | $this->assertEquals( 'id', $col->name() );
316 | $this->assertEquals( 'integer', $col->type() );
317 | $this->assertTrue( $col->seq() );
318 | }
319 |
320 |
321 | public function testIdName()
322 | {
323 | \Aimeos\Upscheme\Schema\Table::macro( 'id', function( string $name = null ) : Column {
324 | return $this->integer( $name ?: 'id' )->seq( true )->primary();
325 | } );
326 |
327 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
328 |
329 | $col = $this->object->id( 'uid' );
330 |
331 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
332 | $this->assertEquals( 'uid', $col->name() );
333 | $this->assertEquals( 'integer', $col->type() );
334 | $this->assertTrue( $col->seq() );
335 | }
336 |
337 |
338 | public function testInt()
339 | {
340 | $col = $this->object->int( 'unittest' );
341 |
342 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
343 | $this->assertEquals( 'unittest', $col->name() );
344 | $this->assertEquals( 'integer', $col->type() );
345 | $this->assertEquals( 0, $col->default() );
346 | }
347 |
348 |
349 | public function testInteger()
350 | {
351 | $col = $this->object->int( 'unittest' );
352 |
353 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
354 | $this->assertEquals( 'unittest', $col->name() );
355 | $this->assertEquals( 'integer', $col->type() );
356 | $this->assertEquals( 0, $col->default() );
357 | }
358 |
359 |
360 | public function testJson()
361 | {
362 | $col = $this->object->json( 'unittest' );
363 |
364 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
365 | $this->assertEquals( 'unittest', $col->name() );
366 | $this->assertEquals( 'json', $col->type() );
367 | }
368 |
369 |
370 | public function testName()
371 | {
372 | $this->tablemock->expects( $this->once() )->method( 'getName' )
373 | ->willReturn( 'unittest' );
374 |
375 | $this->assertEquals( 'unittest', $this->object->name() );
376 | }
377 |
378 |
379 | public function testSmallint()
380 | {
381 | $col = $this->object->smallint( 'unittest' );
382 |
383 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
384 | $this->assertEquals( 'unittest', $col->name() );
385 | $this->assertEquals( 'smallint', $col->type() );
386 | $this->assertEquals( 0, $col->default() );
387 | }
388 |
389 |
390 | public function testString()
391 | {
392 | $col = $this->object->string( 'unittest', 128 );
393 |
394 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
395 | $this->assertEquals( 'unittest', $col->name() );
396 | $this->assertEquals( 'string', $col->type() );
397 | $this->assertEquals( 128, $col->length() );
398 | $this->assertEquals( '', $col->default() );
399 | }
400 |
401 |
402 | public function testText()
403 | {
404 | $col = $this->object->text( 'unittest', 0x7fff );
405 |
406 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
407 | $this->assertEquals( 'unittest', $col->name() );
408 | $this->assertEquals( 'text', $col->type() );
409 | $this->assertEquals( 0x7fff, $col->length() );
410 | $this->assertEquals( '', $col->default() );
411 | }
412 |
413 |
414 | public function testTime()
415 | {
416 | $col = $this->object->time( 'unittest' );
417 |
418 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
419 | $this->assertEquals( 'unittest', $col->name() );
420 | $this->assertEquals( 'time', $col->type() );
421 | }
422 |
423 |
424 | public function testUuid()
425 | {
426 | $col = $this->object->uuid( 'unittest' );
427 |
428 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Column::class, $col );
429 | $this->assertEquals( 'unittest', $col->name() );
430 | $this->assertEquals( 'guid', $col->type() );
431 | }
432 |
433 |
434 | public function testDropColumn()
435 | {
436 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
437 | $this->tablemock->expects( $this->once() )->method( 'dropColumn' );
438 |
439 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->dropColumn( 'unittest' ) );
440 | }
441 |
442 |
443 | public function testDropIndex()
444 | {
445 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
446 | $this->tablemock->expects( $this->once() )->method( 'dropIndex' );
447 |
448 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->dropIndex( 'unittest' ) );
449 | }
450 |
451 |
452 | public function testDropForeign()
453 | {
454 | $this->tablemock->expects( $this->once() )->method( 'hasForeignKey' )->willReturn( true );
455 | $this->tablemock->expects( $this->once() )->method( 'removeForeignKey' );
456 |
457 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->dropForeign( 'unittest' ) );
458 | }
459 |
460 |
461 | public function testDropPrimary()
462 | {
463 | $idx = new \Doctrine\DBAL\Schema\Index( 'PRIMARY', ['id'], true, true );
464 | $this->tablemock->expects( $this->once() )->method( 'getPrimaryKey' )->willReturn( $idx );
465 | $this->tablemock->expects( $this->once() )->method( 'dropPrimaryKey' );
466 |
467 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->dropPrimary( 'unittest' ) );
468 | }
469 |
470 |
471 | public function testHasColumn()
472 | {
473 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
474 | $this->assertTrue( $this->object->hasColumn( 'unittest' ) );
475 | }
476 |
477 |
478 | public function testHasColumnNot()
479 | {
480 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( false );
481 | $this->assertFalse( $this->object->hasColumn( 'unittest' ) );
482 | }
483 |
484 |
485 | public function testHasColumnMultiple()
486 | {
487 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasColumn' )->willReturn( true );
488 | $this->assertTrue( $this->object->hasColumn( ['unittest', 'testunit'] ) );
489 | }
490 |
491 |
492 | public function testHasIndex()
493 | {
494 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
495 | $this->assertTrue( $this->object->hasIndex( 'unittest' ) );
496 | }
497 |
498 |
499 | public function testHasIndexNot()
500 | {
501 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( false );
502 | $this->assertFalse( $this->object->hasIndex( 'unittest' ) );
503 | }
504 |
505 |
506 | public function testHasIndexMultiple()
507 | {
508 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasIndex' )->willReturn( true );
509 | $this->assertTrue( $this->object->hasIndex( ['unittest', 'testunit'] ) );
510 | }
511 |
512 |
513 | public function testHasForeign()
514 | {
515 | $this->tablemock->expects( $this->once() )->method( 'hasForeignKey' )->willReturn( true );
516 | $this->assertTrue( $this->object->hasForeign( 'unittest' ) );
517 | }
518 |
519 |
520 | public function testHasForeignNot()
521 | {
522 | $this->tablemock->expects( $this->once() )->method( 'hasForeignKey' )->willReturn( false );
523 | $this->assertFalse( $this->object->hasForeign( 'unittest' ) );
524 | }
525 |
526 |
527 | public function testHasForeignMultiple()
528 | {
529 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasForeignKey' )->willReturn( true );
530 | $this->assertTrue( $this->object->hasForeign( ['unittest', 'testunit'] ) );
531 | }
532 |
533 |
534 | public function testForeign()
535 | {
536 | $dbalcol = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Column' )
537 | ->disableOriginalConstructor()
538 | ->getMock();
539 |
540 | $dbaltable = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
541 | ->disableOriginalConstructor()
542 | ->getMock();
543 |
544 | $table = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Table' )
545 | ->onlyMethods( ['copyColumn', 'hasColumn'] )
546 | ->setConstructorArgs( [$this->dbmock, $dbaltable] )
547 | ->getMock();
548 |
549 | $table->expects( $this->once() )->method( 'copyColumn' );
550 | $table->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
551 | $dbaltable->expects( $this->once() )->method( 'getColumn' )->willReturn( $dbalcol );
552 |
553 | $this->dbmock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
554 | $this->dbmock->expects( $this->once() )->method( 'table' )->willReturn( $table );
555 |
556 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Foreign::class, $table->foreign( 'pid', 'fktable', 'id', 'fk_pid' ) );
557 | }
558 |
559 |
560 | public function testForeignTableMissing()
561 | {
562 | $this->expectException( 'RuntimeException' );
563 | $this->object->foreign( 'pid', 'fktable', 'id', 'fk_pid' );
564 | }
565 |
566 |
567 | public function testForeignColumnMissing()
568 | {
569 | $this->dbmock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
570 |
571 | $this->expectException( 'RuntimeException' );
572 | $this->object->foreign( 'pid', 'fktable', 'id', 'fk_pid' );
573 | }
574 |
575 |
576 | public function testForeignLocalcolMissing()
577 | {
578 | $dbalcol = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Column' )
579 | ->disableOriginalConstructor()
580 | ->getMock();
581 |
582 | $dbaltable = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
583 | ->disableOriginalConstructor()
584 | ->getMock();
585 |
586 | $table = $this->getMockBuilder( '\Aimeos\Upscheme\Schema\Table' )
587 | ->onlyMethods( ['copyColumn', 'hasColumn'] )
588 | ->setConstructorArgs( [$this->dbmock, $dbaltable] )
589 | ->getMock();
590 |
591 | $table->expects( $this->once() )->method( 'copyColumn' );
592 | $table->expects( $this->exactly( 2 ) )->method( 'hasColumn' )->willReturn( true );
593 | $dbaltable->expects( $this->once() )->method( 'getColumn' )->willReturn( $dbalcol );
594 |
595 | $this->dbmock->expects( $this->once() )->method( 'hasTable' )->willReturn( true );
596 | $this->dbmock->expects( $this->once() )->method( 'table' )->willReturn( $table );
597 |
598 | $this->expectException( 'LogicException' );
599 | $table->foreign( ['pid'], 'fktable', ['id', 'sid'], 'fk_pid' );
600 | }
601 |
602 |
603 | public function testIndex()
604 | {
605 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
606 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
607 | $this->tablemock->expects( $this->once() )->method( 'getIndexes' )->willReturn( [] );
608 |
609 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->index( 'pid' ) );
610 | }
611 |
612 |
613 | public function testIndexName()
614 | {
615 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
616 |
617 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->index( 'pid', 'idx_pid' ) );
618 | }
619 |
620 |
621 | public function testIndexExists()
622 | {
623 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
624 | ->onlyMethods( ['getColumns'] )
625 | ->disableOriginalConstructor()
626 | ->getMock();
627 |
628 | $idxmock->expects( $this->once() )->method( 'getColumns' )->willReturn( ['pid'] );
629 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
630 | $this->tablemock->expects( $this->once() )->method( 'getIndexes' )->willReturn( [$idxmock] );
631 |
632 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->index( 'pid' ) );
633 | }
634 |
635 |
636 | public function testIndexExistsName()
637 | {
638 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
639 | ->onlyMethods( ['spansColumns'] )
640 | ->disableOriginalConstructor()
641 | ->getMock();
642 |
643 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( true );
644 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
645 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
646 |
647 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->index( 'pid', 'idx_pid' ) );
648 | }
649 |
650 |
651 | public function testIndexChange()
652 | {
653 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
654 | ->onlyMethods( ['spansColumns'] )
655 | ->disableOriginalConstructor()
656 | ->getMock();
657 |
658 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( false );
659 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
660 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
661 | $this->tablemock->expects( $this->once() )->method( 'dropIndex' );
662 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
663 |
664 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->index( 'pid', 'idx_pid' ) );
665 | }
666 |
667 |
668 | public function testPrimary()
669 | {
670 | $this->tablemock->expects( $this->once() )->method( 'setPrimaryKey' );
671 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
672 |
673 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->primary( 'id' ) );
674 | }
675 |
676 |
677 | public function testPrimaryName()
678 | {
679 | $this->tablemock->expects( $this->once() )->method( 'setPrimaryKey' );
680 |
681 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->primary( 'id', 'pk_id' ) );
682 | }
683 |
684 |
685 | public function testPrimaryExists()
686 | {
687 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
688 | ->onlyMethods( ['spansColumns'] )
689 | ->disableOriginalConstructor()
690 | ->getMock();
691 |
692 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( true );
693 | $this->tablemock->expects( $this->once() )->method( 'getPrimaryKey' )->willReturn( $idxmock );
694 |
695 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->primary( 'id', 'pk_id' ) );
696 | }
697 |
698 |
699 | public function testPrimaryChange()
700 | {
701 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
702 | ->onlyMethods( ['spansColumns'] )
703 | ->disableOriginalConstructor()
704 | ->getMock();
705 |
706 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( false );
707 | $this->tablemock->expects( $this->once() )->method( 'getPrimaryKey' )->willReturn( $idxmock );
708 | $this->tablemock->expects( $this->once() )->method( 'dropPrimaryKey' );
709 | $this->tablemock->expects( $this->once() )->method( 'setPrimaryKey' );
710 |
711 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->primary( 'id', 'pk_id' ) );
712 | }
713 |
714 |
715 | public function testRenameIndex()
716 | {
717 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->renameIndex( 'idx_test' ) );
718 | }
719 |
720 |
721 | public function testRenameIndexExists()
722 | {
723 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
724 | $this->tablemock->expects( $this->once() )->method( 'renameIndex' );
725 |
726 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->renameIndex( 'idx_t1', 'idx_t2' ) );
727 | }
728 |
729 |
730 | public function testRenameIndexMultiple()
731 | {
732 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'hasIndex' )->willReturn( true );
733 | $this->tablemock->expects( $this->exactly( 2 ) )->method( 'renameIndex' );
734 |
735 | $idx = ['idx_t1' => 'idx_t2', 'idx_t3' => 'idx_t4'];
736 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->renameIndex( $idx ) );
737 | }
738 |
739 |
740 | public function testRenameIndexName()
741 | {
742 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
743 | ->onlyMethods( ['getColumns'] )
744 | ->disableOriginalConstructor()
745 | ->getMock();
746 |
747 | $idxmock->expects( $this->once() )->method( 'getColumns' )->willReturn( ['a', 'b'] );
748 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
749 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
750 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
751 | $this->tablemock->expects( $this->once() )->method( 'renameIndex' );
752 |
753 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->renameIndex( 'idx_test' ) );
754 | }
755 |
756 |
757 | public function testRenameColumn()
758 | {
759 | $this->tablemock->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
760 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
761 | $this->dbmock->expects( $this->once() )->method( 'renameColumn' );
762 |
763 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->renameColumn( 'test' ) );
764 | }
765 |
766 |
767 | public function testSpatial()
768 | {
769 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
770 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
771 |
772 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->spatial( 'pid' ) );
773 | }
774 |
775 |
776 | public function testSpatialName()
777 | {
778 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
779 |
780 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->spatial( 'pid', 'idx_pid' ) );
781 | }
782 |
783 |
784 | public function testSpatialExists()
785 | {
786 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
787 | ->onlyMethods( ['hasFlag', 'spansColumns'] )
788 | ->disableOriginalConstructor()
789 | ->getMock();
790 |
791 | $idxmock->expects( $this->once() )->method( 'hasFlag' )->willReturn( true );
792 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( true );
793 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
794 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
795 |
796 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->spatial( 'pid', 'idx_pid' ) );
797 | }
798 |
799 |
800 | public function testSpatialChange()
801 | {
802 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
803 | ->onlyMethods( ['hasFlag', 'spansColumns'] )
804 | ->disableOriginalConstructor()
805 | ->getMock();
806 |
807 | $idxmock->expects( $this->once() )->method( 'hasFlag' )->willReturn( true );
808 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( false );
809 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
810 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
811 | $this->tablemock->expects( $this->once() )->method( 'dropIndex' );
812 | $this->tablemock->expects( $this->once() )->method( 'addIndex' );
813 |
814 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->spatial( 'pid', 'idx_pid' ) );
815 | }
816 |
817 |
818 | public function testUnique()
819 | {
820 | $this->tablemock->expects( $this->once() )->method( 'addUniqueIndex' );
821 | $this->tablemock->expects( $this->once() )->method( 'getName' )->willReturn( 'test' );
822 |
823 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->unique( 'pid' ) );
824 | }
825 |
826 |
827 | public function testUniqueName()
828 | {
829 | $this->tablemock->expects( $this->once() )->method( 'addUniqueIndex' );
830 |
831 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->unique( 'pid', 'unq_pid' ) );
832 | }
833 |
834 |
835 | public function testUniqueExists()
836 | {
837 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
838 | ->onlyMethods( ['isUnique', 'spansColumns'] )
839 | ->disableOriginalConstructor()
840 | ->getMock();
841 |
842 | $idxmock->expects( $this->once() )->method( 'isUnique' )->willReturn( true );
843 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( true );
844 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
845 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
846 |
847 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->unique( 'pid', 'unq_pid' ) );
848 | }
849 |
850 |
851 | public function testUniqueChange()
852 | {
853 | $idxmock = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Index' )
854 | ->onlyMethods( ['isUnique', 'spansColumns'] )
855 | ->disableOriginalConstructor()
856 | ->getMock();
857 |
858 | $idxmock->expects( $this->once() )->method( 'isUnique' )->willReturn( true );
859 | $idxmock->expects( $this->once() )->method( 'spansColumns' )->willReturn( false );
860 | $this->tablemock->expects( $this->once() )->method( 'getIndex' )->willReturn( $idxmock );
861 | $this->tablemock->expects( $this->once() )->method( 'hasIndex' )->willReturn( true );
862 | $this->tablemock->expects( $this->once() )->method( 'dropIndex' );
863 | $this->tablemock->expects( $this->once() )->method( 'addUniqueIndex' );
864 |
865 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->unique( 'pid', 'unq_pid' ) );
866 | }
867 |
868 |
869 | public function testUp()
870 | {
871 | $this->dbmock->expects( $this->once() )->method( 'up' );
872 |
873 | $this->assertInstanceOf( \Aimeos\Upscheme\Schema\Table::class, $this->object->up() );
874 | }
875 |
876 |
877 | public function testCopyColumn()
878 | {
879 | $dbalcol = new \Doctrine\DBAL\Schema\Column( 'unittest2', \Doctrine\DBAL\Types\Type::getType( 'string' ) );
880 |
881 | $dbaltable = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
882 | ->disableOriginalConstructor()
883 | ->onlyMethods( ['addColumn'] )
884 | ->getMock();
885 |
886 | $dbaltable->expects( $this->once() )->method( 'addColumn' );
887 |
888 | $object = new \Aimeos\Upscheme\Schema\Table( $this->dbmock, $dbaltable );
889 |
890 | $this->access( 'copyColumn' )->invokeArgs( $object, [$dbalcol, 'unittest'] );
891 | }
892 |
893 |
894 | public function testCopyColumnExisting()
895 | {
896 | $dbalcol = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Column' )
897 | ->disableOriginalConstructor()
898 | ->onlyMethods( ['toArray'] )
899 | ->getMock();
900 |
901 | $dbaltable = $this->getMockBuilder( '\Doctrine\DBAL\Schema\Table' )
902 | ->onlyMethods( ['modifyColumn', 'hasColumn'] )
903 | ->disableOriginalConstructor()
904 | ->getMock();
905 |
906 | $dbalcol->expects( $this->once() )->method( 'toArray' )->willReturn( [] );
907 | $dbaltable->expects( $this->once() )->method( 'hasColumn' )->willReturn( true );
908 | $dbaltable->expects( $this->once() )->method( 'modifyColumn' );
909 |
910 | $object = new \Aimeos\Upscheme\Schema\Table( $this->dbmock, $dbaltable );
911 |
912 | $this->access( 'copyColumn' )->invokeArgs( $object, [$dbalcol, 'unittest'] );
913 | }
914 |
915 |
916 | protected function access( $name )
917 | {
918 | $class = new \ReflectionClass( \Aimeos\Upscheme\Schema\Table::class );
919 | $method = $class->getMethod( $name );
920 | $method->setAccessible( true );
921 |
922 | return $method;
923 | }
924 | }
925 |
--------------------------------------------------------------------------------