├── src
├── Form
│ └── .gitkeep
├── Helper
│ ├── .gitkeep
│ ├── HTTP
│ │ ├── Request
│ │ │ ├── RequestAdaptorInterface.php
│ │ │ ├── Factory
│ │ │ │ ├── FactoryInterface.php
│ │ │ │ └── Type
│ │ │ │ │ └── ServerFactory.php
│ │ │ └── Request.php
│ │ ├── Validation
│ │ │ ├── InterfaceValidator.php
│ │ │ ├── AbstractType.php
│ │ │ ├── Type
│ │ │ │ ├── PatternValidator.php
│ │ │ │ ├── MethodValidator.php
│ │ │ │ ├── ControllerValidator.php
│ │ │ │ └── ActionValidator.php
│ │ │ └── Validation.php
│ │ ├── Response
│ │ │ └── View.php
│ │ ├── Route
│ │ │ ├── Router.php
│ │ │ ├── Validator.php
│ │ │ ├── Factory.php
│ │ │ └── Route.php
│ │ ├── URI
│ │ │ └── URIBuilder.php
│ │ └── Locator
│ │ │ └── Locator.php
│ └── Kernel
│ │ └── Kernel.php
├── Controller
│ ├── AbstractController.php
│ ├── Home.php
│ └── Invoice.php
├── Manager
│ ├── ManagerInterface.php
│ ├── AbstractManager.php
│ ├── StatusManager.php
│ ├── InvoiceManager.php
│ ├── CustomerManager.php
│ └── InvoiceItemManager.php
├── DB
│ ├── Builder
│ │ ├── CollectionInterface.php
│ │ ├── Builder.php
│ │ ├── FieldCollection.php
│ │ └── WhereCollection.php
│ ├── QueryBuilderInterface.php
│ ├── QueryCreate.php
│ ├── Connection.php
│ └── QueryBuilder.php
├── Repository
│ ├── RepositoryInterface.php
│ ├── AbstractRepository.php
│ ├── RepositoryFactory.php
│ ├── CustomerRepository.php
│ ├── InvoiceRepository.php
│ ├── InvoiceItemRepository.php
│ └── StatusRepository.php
├── Hydration
│ ├── StatusHydrator.php
│ ├── InvoiceHydrator.php
│ ├── CustomerHydrator.php
│ ├── AbstractHydrator.php
│ └── InvoiceItemHydrator.php
└── Entity
│ ├── GenericEntityInterface.php
│ ├── Status.php
│ ├── AbstractEntity.php
│ ├── Customer.php
│ ├── InvoiceItem.php
│ └── Invoice.php
├── tests
├── _data
│ ├── .gitkeep
│ └── dump.sql
├── _output
│ └── .gitignore
├── _support
│ ├── _generated
│ │ └── .gitignore
│ ├── Helper
│ │ ├── Unit.php
│ │ ├── Acceptance.php
│ │ ├── Functional.php
│ │ └── Integration.php
│ ├── Code.php
│ ├── UnitTester.php
│ ├── AcceptanceTester.php
│ └── FunctionalTester.php
├── .DS_Store
├── unit.suite.yml
├── functional.suite.yml
├── integration.suite.yml
├── acceptance
│ ├── HomePageCest.php
│ ├── NotFoundPageCest.php
│ ├── DBStatusCest.php
│ ├── InvoicePageCest.php
│ ├── DBStatusEntityCest.php
│ ├── DBCustomerEntityCest.php
│ └── DBInvoiceEntityCest.php
├── acceptance.suite.yml
├── unit
│ ├── Entity
│ │ ├── AddCustomerToInvoiceTest.php
│ │ ├── SetStatusToInvoiceTest.php
│ │ ├── StatusTest.php
│ │ ├── InvoiceTest.php
│ │ ├── CustomerTest.php
│ │ ├── AddInvoiceItemToInvoiceTest.php
│ │ └── InvoiceItemTest.php
│ ├── DB
│ │ ├── ConnectionTest.php
│ │ ├── BuilderTest.php
│ │ ├── StatusQueryBuilderTest.php
│ │ ├── FieldCollectionTest.php
│ │ ├── WhereCollectionTest.php
│ │ └── QueryBuilderTest.php
│ ├── Hydrator
│ │ ├── StatusTest.php
│ │ ├── InvoiceTest.php
│ │ ├── CustomerTest.php
│ │ └── InvoiceItemTest.php
│ └── Helper
│ │ └── HTTP
│ │ ├── Validation
│ │ ├── Type
│ │ │ ├── PatternValidatorTest.php
│ │ │ ├── ActionValidatorTest.php
│ │ │ ├── ControllerValidatorTest.php
│ │ │ └── MethodValidatorTest.php
│ │ └── ValidationTest.php
│ │ ├── URI
│ │ └── URIBuilderTest.php
│ │ ├── Route
│ │ ├── RouteTest.php
│ │ ├── RouterTest.php
│ │ └── FactoryTest.php
│ │ ├── Request
│ │ ├── Factory
│ │ │ └── Type
│ │ │ │ └── ServerFactoryTest.php
│ │ └── RequestTest.php
│ │ └── Locator
│ │ └── LocatorTest.php
└── integration
│ ├── DBStatusEntityTest.php
│ ├── DBInvoiceEntityTest.php
│ ├── DBCustomerEntityTest.php
│ └── DBInvoiceItemEntityTest.php
├── public
├── assets
│ └── css
│ │ └── .gitkeep
├── views
│ ├── home
│ │ └── index.php
│ ├── invoice
│ │ ├── edit.php
│ │ ├── index.php
│ │ └── dashboard.php
│ └── templates
│ │ ├── primary-menu.php
│ │ └── base.php
├── 404.html
├── index.php
└── .htaccess
├── .gitignore
├── mysql
├── data
│ └── status.sql
├── rebuild.sql
└── schema
│ ├── status.sql
│ ├── customer.sql
│ ├── invoice.sql
│ └── invoice_item.sql
├── .env.dist
├── app
└── config
│ ├── config.php
│ ├── bootstrap.php
│ └── routing.php
├── codeception.yml
├── composer.json
├── apache
└── conf
│ └── 000-default.conf
├── LICENSE
├── docker-compose.yml
├── Dockerfile
├── notes.php
└── README.md
/src/Form/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Helper/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/_data/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/css/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/_output/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/tests/_support/_generated/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | !.gitkeep
3 | .env
4 | /vendor/
5 | notes.php
6 |
--------------------------------------------------------------------------------
/tests/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfwd/freecodecamp-PHP-OOP/HEAD/tests/.DS_Store
--------------------------------------------------------------------------------
/public/views/home/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 |
5 |
6 | Invoices
7 |
8 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Page Not Found
6 |
7 |
8 | Page Not Found
9 |
10 |
--------------------------------------------------------------------------------
/public/views/invoice/dashboard.php:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/src/DB/Builder/CollectionInterface.php:
--------------------------------------------------------------------------------
1 | getCode() === 404) {
10 | header("HTTP/1.0 404 Not Found");
11 | exit;
12 | }
13 | }
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 | DirectoryIndex index.php
2 | ErrorDocument 404 /var/www/html/public/404.html
3 |
4 | # Enable rewrite engine
5 | RewriteEngine on
6 |
7 | # Set the base
8 | RewriteBase /
9 |
10 | # Deliver files and folders if they exist
11 | RewriteCond %{REQUEST_FILENAME} !-f
12 | RewriteCond %{REQUEST_FILENAME} !-d
13 |
14 | # Push every request to the index.php
15 | RewriteRule ^(.*)$ index.php [QSA]
16 |
--------------------------------------------------------------------------------
/src/Manager/AbstractManager.php:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | Home Page
7 |
8 |
9 |
10 |
15 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/functional.suite.yml:
--------------------------------------------------------------------------------
1 | # Codeception Test Suite Configuration
2 | #
3 | # Suite for functional tests
4 | # Emulate web requests and make application process them
5 | # Include one of framework modules (Symfony2, Yii2, Laravel5) to use it
6 | # Remove this suite if you don't use frameworks
7 |
8 | actor: FunctionalTester
9 | modules:
10 | enabled:
11 | # add a framework module here
12 | - \Helper\Functional
13 | step_decorators: ~
--------------------------------------------------------------------------------
/src/Helper/HTTP/Validation/AbstractType.php:
--------------------------------------------------------------------------------
1 | route = $route;
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/tests/integration.suite.yml:
--------------------------------------------------------------------------------
1 | actor: Code
2 | modules:
3 | enabled:
4 | - \Helper\Integration
5 | - Asserts
6 | - Db
7 | config:
8 | Db:
9 | dsn: 'mysql:host=db_test;dbname=test_invoice_app'
10 | user: 'root'
11 | password: 'test'
12 | reconnect: true
13 | dump: 'tests/_data/dump.sql'
14 | populate: true
15 | cleanup: true
16 | step_decorators: ~
--------------------------------------------------------------------------------
/src/Helper/HTTP/Response/View.php:
--------------------------------------------------------------------------------
1 | route->getPattern());
14 | }
15 |
16 |
17 | }
--------------------------------------------------------------------------------
/apache/conf/000-default.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 | ServerAdmin webmaster@localhost
4 | ServerName invoice-test
5 | DocumentRoot ${APACHE_DOCUMENT_ROOT}
6 |
7 |
8 | Options Indexes FollowSymLinks
9 | AllowOverride All
10 | Require all granted
11 |
12 |
13 | ErrorLog ${APACHE_LOG_DIR}/error.log
14 | CustomLog ${APACHE_LOG_DIR}/access.log combined
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Hydration/StatusHydrator.php:
--------------------------------------------------------------------------------
1 | setName($data['name'])
13 | ->setInternalName($data['internal_name'])
14 | ;
15 | parent::hydrateRest($data, $entity);
16 |
17 | return $entity;
18 |
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Route/Router.php:
--------------------------------------------------------------------------------
1 | getPattern()] = $route;
16 | }
17 |
18 | /**
19 | * @return array
20 | */
21 | public function getRoutes():array
22 | {
23 | return self::$routes;
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/URI/URIBuilder.php:
--------------------------------------------------------------------------------
1 | $pattern) {
15 | $URI = str_replace('{'.$parameter.'}', $pattern, $URI);
16 | }
17 | return '#^' .$URI.'$#';
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/Hydration/InvoiceHydrator.php:
--------------------------------------------------------------------------------
1 | setReference($data['reference'])
13 | ->setTotal($data['total'])
14 | ->setVAT($data['vat'])
15 | ;
16 | parent::hydrateRest($data, $entity);
17 |
18 | return $entity;
19 |
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/tests/acceptance/HomePageCest.php:
--------------------------------------------------------------------------------
1 | wantTo('Test the response code for the homepage');
17 | $I->amOnPage('/');
18 | $I->seeResponseCodeIs(200);
19 | $I->canSee('Hello from the homepage');
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Hydration/CustomerHydrator.php:
--------------------------------------------------------------------------------
1 | setCompanyName($data['company_name'])
13 | ->setFirstName($data['first_name'])
14 | ->setLastName($data['last_name'])
15 | ;
16 | parent::hydrateRest($data, $entity);
17 |
18 | return $entity;
19 |
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/tests/acceptance/NotFoundPageCest.php:
--------------------------------------------------------------------------------
1 | wantTo('Test the response code for 404 page');
17 | $I->amOnPage('/not-found');
18 | $I->seeResponseCodeIs(404);
19 | // $I->canSee('Page Not Found');
20 | // $I->canSeeInTitle('Page Not Found');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Hydration/AbstractHydrator.php:
--------------------------------------------------------------------------------
1 | setDateCreated($dateCreated);
16 | $entity->setDateUpdated($dateUpdate);
17 | $entity->setId($data['id']);
18 | return $entity;
19 |
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/mysql/schema/invoice.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `invoice`;
2 |
3 | CREATE TABLE IF NOT EXISTS `invoice` (
4 | `id` INT (11) NOT NULL auto_increment,
5 | `reference` VARCHAR (250) NOT NULL,
6 | `total` DECIMAL,
7 | `vat` DECIMAL,
8 | `status_id` INT(11),
9 | `customer_id` INT(11) NULL,
10 | `date_created` DATETIME DEFAULT CURRENT_TIMESTAMP,
11 | `date_updated` DATETIME ON UPDATE CURRENT_TIMESTAMP,
12 | FOREIGN KEY fk_status(status_id)
13 | REFERENCES status(id),
14 | FOREIGN KEY fk_customer(customer_id)
15 | REFERENCES customer(id),
16 | PRIMARY KEY (id)
17 | );
18 |
--------------------------------------------------------------------------------
/mysql/schema/invoice_item.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `invoice_item`;
2 |
3 | CREATE TABLE IF NOT EXISTS `invoice_item` (
4 | `id` INT (11) NOT NULL auto_increment,
5 | `reference` VARCHAR (250) NULL,
6 | `description` VARCHAR (250) NOT NULL,
7 | `unit_price` DECIMAL NOT NULL,
8 | `units` INT(11) NOT NULL,
9 | `total` DECIMAL NOT NULL,
10 | `invoice_id` INT(11) NOT NULL,
11 | `date_created` DATETIME DEFAULT CURRENT_TIMESTAMP,
12 | `date_updated` DATETIME ON UPDATE CURRENT_TIMESTAMP,
13 | FOREIGN KEY fk_invoice(invoice_id)
14 | REFERENCES invoice(id),
15 | PRIMARY KEY (id)
16 | );
17 |
--------------------------------------------------------------------------------
/src/Hydration/InvoiceItemHydrator.php:
--------------------------------------------------------------------------------
1 | setReference($data['reference'])
13 | ->setDescription($data['description'])
14 | ->setTotal($data['total'])
15 | ->setUnitPrice($data['unit_price'])
16 | ->setUnits($data['units'])
17 | ;
18 | parent::hydrateRest($data, $entity);
19 |
20 | return $entity;
21 |
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/tests/_support/Code.php:
--------------------------------------------------------------------------------
1 | setValidators([
14 | new Type\PatternValidator(),
15 | new Type\ControllerValidator(),
16 | new Type\ActionValidator()
17 | ]);
18 |
19 | // Make routes
20 | $routeFactory = new Factory();
21 | $routes = $routeFactory->makeRoutes($routeData);
22 | Validator::validate($routes, $validation);
23 |
24 | return $routes;
25 |
--------------------------------------------------------------------------------
/tests/acceptance.suite.yml:
--------------------------------------------------------------------------------
1 | # Codeception Test Suite Configuration
2 | #
3 | # Suite for acceptance tests.
4 | # Perform tests in browser using the WebDriver or PhpBrowser.
5 | # If you need both WebDriver and PHPBrowser tests - create a separate suite.
6 |
7 | actor: AcceptanceTester
8 | modules:
9 | enabled:
10 | - PhpBrowser:
11 | url: http://localhost
12 | - \Helper\Acceptance
13 | - Db
14 | config:
15 | Db:
16 | dsn: 'mysql:host=db_test;dbname=test_invoice_app'
17 | user: 'root'
18 | password: 'test'
19 | reconnect: true
20 | dump: 'tests/_data/dump.sql'
21 | populate: true
22 | cleanup: true
23 | step_decorators: ~
--------------------------------------------------------------------------------
/src/Controller/Invoice.php:
--------------------------------------------------------------------------------
1 | $request->getParameter('id')
13 | ]
14 | );
15 | }
16 |
17 | public function edit(Request $request)
18 | {
19 | return View::render('views/invoice/edit.php',[
20 | 'id' => $request->getParameter('id')
21 | ]
22 | );
23 | }
24 |
25 | public function dashboard(Request $request)
26 | {
27 | return View::render('views/invoice/dashboard.php');
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/unit/Entity/AddCustomerToInvoiceTest.php:
--------------------------------------------------------------------------------
1 | setCustomer($customer);
31 | $this->assertInstanceOf(Customer::class, $invoice->getCustomer());
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/tests/unit/DB/ConnectionTest.php:
--------------------------------------------------------------------------------
1 | getCreds();
31 |
32 | $this->assertSame('db_test', getenv('TEST_MYSQL_HOST'));
33 | $this->assertSame('db_test', $creds['host']);
34 |
35 |
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Route/Validator.php:
--------------------------------------------------------------------------------
1 | validate($route);
22 | if(false === $isValid) {
23 | throw new Exception('Validator failed '. get_class($route));
24 | break;
25 | }
26 | }
27 |
28 | return $isValid;
29 | }
30 |
31 |
32 |
33 | }
--------------------------------------------------------------------------------
/src/Entity/GenericEntityInterface.php:
--------------------------------------------------------------------------------
1 | setName('draft');
30 | $invoice = new Invoice();
31 | $invoice->setStatus($status);
32 | $this->assertInstanceOf(Status::class, $invoice->getStatus());
33 | $this->assertSame('draft', $invoice->getStatus()->getName());
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/Helper/Kernel/Kernel.php:
--------------------------------------------------------------------------------
1 | locate();
23 |
24 | if(false === $route instanceof Route) {
25 | throw new Exception('Cannot find page', 404);
26 | }
27 | $controllerName = $route->getController();
28 |
29 | $controller = new $controllerName();
30 |
31 | return $controller->{$route->getAction()}($request);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/config/routing.php:
--------------------------------------------------------------------------------
1 | '/',
8 | 'controller' => Controller\Home::class,
9 | 'method' => ['GET'],
10 | 'action' => 'index'
11 | ],
12 | [
13 | 'pattern' => '/invoice',
14 | 'controller' => Controller\Invoice::class,
15 | 'method' => ['GET'],
16 | 'action' => 'dashboard'
17 | ],
18 | [
19 | 'pattern' => '/invoice/{id}',
20 | 'controller' => Controller\Invoice::class,
21 | 'method' => ['GET'],
22 | 'action' => 'index',
23 | 'parameters' => [
24 | 'id' => '([0-9]*)'
25 | ]
26 | ],
27 | [
28 | 'pattern' => '/invoice/{id}/edit',
29 | 'controller' => Controller\Invoice::class,
30 | 'method' => ['GET'],
31 | 'action' => 'edit',
32 | 'parameters' => [
33 | 'id' => '([0-9]*)'
34 | ]
35 | ],
36 | ];
--------------------------------------------------------------------------------
/src/Helper/HTTP/Request/Factory/Type/ServerFactory.php:
--------------------------------------------------------------------------------
1 | setPath($_SERVER['REQUEST_URI']);
28 | $request->setMethod($_SERVER['REQUEST_METHOD']);
29 |
30 | return $request;
31 |
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Route/Factory.php:
--------------------------------------------------------------------------------
1 | routes[] = $this->addRoute($routeData);
21 | }
22 |
23 | return $this->routes;
24 |
25 | }
26 |
27 | /**
28 | * @param array $options
29 | *
30 | * @return null|Route
31 | */
32 | public function addRoute(array $options):?Route
33 | {
34 | $route = new Route();
35 | $route->setAction($options['action'])
36 | ->setController($options['controller'])
37 | ->setMethods($options['method'])
38 | ->setPattern($options['pattern'])
39 | ->setParameters((isset($options['parameters']) ? $options['parameters'] : []) )
40 | ;
41 |
42 | return $route;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Validation/Type/MethodValidator.php:
--------------------------------------------------------------------------------
1 | route->getMethods();
16 |
17 | if(is_array($value)) {
18 | $isValid = $this->isValueCorrect($value);
19 | }
20 |
21 | return $isValid;
22 | }
23 |
24 | /**
25 | * @param array $values
26 | *
27 | * @return bool
28 | */
29 | public function isValueCorrect(array $values)
30 | {
31 | $possible = [
32 | 'GET',
33 | 'POST'
34 | ];
35 |
36 | foreach($values as $value) {
37 | if(false === in_array($value, $possible)) {
38 | return false;
39 | }
40 | }
41 |
42 | return true;
43 | }
44 |
45 |
46 |
47 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Validation/Type/ControllerValidator.php:
--------------------------------------------------------------------------------
1 | route->getController();
19 |
20 | if(false === empty($className)) {
21 | $isValid = $this->doesExist($className);
22 | }
23 |
24 | return $isValid;
25 | }
26 |
27 | /**
28 | * @param string $string
29 | *
30 | * @return bool
31 | */
32 | public function doesExist(string $string): bool
33 | {
34 | $isValid = true;
35 | try {
36 | new ReflectionClass($string);
37 | } catch (ReflectionException $exception){
38 | $isValid = false;
39 | }
40 | return $isValid;
41 |
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Peter Fisher
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/DB/Builder/Builder.php:
--------------------------------------------------------------------------------
1 | collections as $collection) {
16 | if($collection instanceof $className) {
17 | $found = $collection;
18 | break;
19 | }
20 | }
21 |
22 | if(FALSE === $found instanceof CollectionInterface) {
23 | $found = new $className();
24 | $this->collections[] = $found;
25 | }
26 |
27 | return $found;
28 | }
29 |
30 | /**
31 | * @return WhereCollection
32 | */
33 | public function where(): WhereCollection
34 | {
35 |
36 | return $this->make(WhereCollection::class);
37 | }
38 |
39 | /**
40 | * @return FieldCollection
41 | */
42 | public function fields(): FieldCollection
43 | {
44 | return $this->make(FieldCollection::class);
45 | }
46 | }
--------------------------------------------------------------------------------
/tests/acceptance/DBStatusCest.php:
--------------------------------------------------------------------------------
1 | seeInDatabase('status', [
18 | 'name' => 'draft',
19 | 'internal_name' => 'DRAFT'
20 | ]);
21 | }
22 |
23 | /**
24 | * @param AcceptanceTester $I
25 | * @group db
26 | * @group db-status-sent
27 | */
28 | public function StatusSentTest(AcceptanceTester $I)
29 | {
30 | $I->seeInDatabase('status', [
31 | 'name' => 'sent',
32 | 'internal_name' => 'SENT'
33 | ]);
34 | }
35 |
36 | /**
37 | * @param AcceptanceTester $I
38 | * @group db
39 | * @group db-status-overdue
40 | */
41 | public function OverdueSentTest(AcceptanceTester $I)
42 | {
43 | $I->seeInDatabase('status', [
44 | 'name' => 'overdue',
45 | 'internal_name' => 'OVERDUE'
46 | ]);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Entity/Status.php:
--------------------------------------------------------------------------------
1 | name;
22 | }
23 |
24 | /**
25 | * @return string
26 | */
27 | public function getInternalName(): string
28 | {
29 | return $this->internalName;
30 | }
31 |
32 | /**
33 | * @param string $name
34 | * @return Status
35 | */
36 | public function setName(string $name): Status
37 | {
38 | $this->name = $name;
39 | $this->setInternalName($name);
40 | return $this;
41 | }
42 |
43 | /**
44 | * @param string $internalName
45 | * @return Status
46 | */
47 | public function setInternalName(string $internalName): Status
48 | {
49 | $this->internalName = strtoupper(str_replace(' ', '_', $internalName));
50 | return $this;
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | volumes:
4 | mysql_data:
5 | mysql_data_test:
6 |
7 | networks:
8 | php_course:
9 |
10 | services:
11 | web:
12 | build:
13 | context: .
14 | ports:
15 | - 80:80
16 | volumes:
17 | - .:/var/www/html
18 | - ./apache/conf/000-default.conf:/etc/apache2/sites-enabled/000-default.conf
19 | - ./apache/php/php.ini:/usr/local/etc/php/php.ini
20 |
21 | environment:
22 | - APACHE_DOCUMENT_ROOT=/var/www/html/public
23 | env_file:
24 | - .env
25 | restart: always
26 | networks:
27 | - php_course
28 |
29 | db:
30 | image: mysql:5.7
31 | volumes:
32 | - mysql_data:/var/lib/mysql
33 | - ./mysql:/scripts
34 | environment:
35 | - MYSQL_DATABASE=${DEV_MYSQL_DATABASE}
36 | - MYSQL_ROOT_PASSWORD=${DEV_MYSQL_ROOT_PASSWORD}
37 | networks:
38 | - php_course
39 | restart: always
40 |
41 |
42 | db_test:
43 | image: mysql:5.7
44 | volumes:
45 | - mysql_data_test:/var/lib/mysql
46 | - ./mysql:/scripts
47 | environment:
48 | - MYSQL_DATABASE=${TEST_MYSQL_DATABASE}
49 | - MYSQL_ROOT_PASSWORD=${TEST_MYSQL_ROOT_PASSWORD}
50 | networks:
51 | - php_course
52 | restart: always
53 |
--------------------------------------------------------------------------------
/src/Helper/HTTP/Validation/Type/ActionValidator.php:
--------------------------------------------------------------------------------
1 | route->getController();
17 | $actionName = $this->route->getAction();
18 |
19 | $isValid = false;
20 | if(false === empty($actionName) && false === empty($className)) {
21 | $isValid = $this->doesExist($className, $actionName);
22 | }
23 |
24 | return $isValid;
25 | }
26 |
27 | /**
28 | * @param string $className
29 | * @param string $actionName
30 | *
31 | * @return bool
32 | */
33 | public function doesExist(string $className, string $actionName):bool
34 | {
35 | $isValid = true;
36 | try {
37 | new ReflectionMethod($className, $actionName);
38 | } catch (ReflectionException $exception){
39 | $isValid = false;
40 | }
41 |
42 | return $isValid;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/DB/Builder/FieldCollection.php:
--------------------------------------------------------------------------------
1 | fields[] = $name;
19 | return $this;
20 | }
21 |
22 | /**
23 | * @param array $fields
24 | * @return FieldCollection
25 | */
26 | public function set(array $fields = []):CollectionInterface
27 | {
28 | $this->fields = [];
29 | for ($i = 0; $i < count($fields); $i++) {
30 | $this->add($fields[$i]);
31 | }
32 | return $this;
33 | }
34 |
35 | /**
36 | * @return string
37 | */
38 | public function getSQL(): string
39 | {
40 | $sql = '*';
41 | if(count($this->fields) > 0) {
42 | $sql = '';
43 | for ($i = 0; $i < count($this->fields); $i++) {
44 | if ($i > 0) {
45 | $sql .= ", ";
46 | }
47 | $sql .= "`" . $this->fields[$i] . "`";
48 | }
49 | }
50 | return $sql;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Repository/AbstractRepository.php:
--------------------------------------------------------------------------------
1 | connection = $connection;
27 | $this->queryBuilder = $queryBuilder;
28 | }
29 |
30 | /**
31 | * Find one entity by ID
32 | *
33 | * @param int $id
34 | * @return mixed
35 | */
36 | abstract public function findOne(int $id);
37 |
38 |
39 | /**
40 | * @param string $tableName
41 | * @return array
42 | */
43 | public function findAll(string $tableName): array
44 | {
45 | $this->queryBuilder->select($tableName);
46 | $sql = $this->queryBuilder->getSQL();
47 |
48 | $dbCon = $this->connection->open();
49 | $statement = $dbCon->prepare($sql);
50 | $statement->execute();
51 |
52 | return $statement->fetchAll();
53 | }
54 | }
--------------------------------------------------------------------------------
/tests/unit/Hydrator/StatusTest.php:
--------------------------------------------------------------------------------
1 | 1,
31 | 'name' => 'test',
32 | 'internal_name' => 'TEST',
33 | 'date_created' => $date->format(DateTime::ISO8601),
34 | 'date_updated' => $date->format(DateTime::ISO8601)
35 | ];
36 | $entity = StatusHydrator::hydrate($data);
37 |
38 | $this->assertInstanceOf(Status::class, $entity);
39 | $this->assertSame(1, $entity->getId());
40 | $this->assertSame('test', $entity->getName());
41 | $this->assertSame('TEST', $entity->getInternalName());
42 | $this->assertInstanceOf(DateTime::class, $entity->getDateCreated());
43 | $this->assertInstanceOf(DateTime::class, $entity->getDateUpdated());
44 | }
45 | }
--------------------------------------------------------------------------------
/src/DB/QueryCreate.php:
--------------------------------------------------------------------------------
1 | builder = $builder;
14 | }
15 |
16 | private $sql = '';
17 |
18 | /**
19 | * @param string $tableName
20 | * @param array $fields
21 | * @return QueryBuilderInterface
22 | */
23 | public function select(string $tableName, array $fields = []): QueryBuilderInterface
24 | {
25 | $fieldCollection = $this->builder->fields()->set($fields);
26 |
27 | $this->sql.= "SELECT " . $fieldCollection->getSQL() . " FROM `" . $tableName . "` ";
28 |
29 | return $this;
30 | }
31 |
32 |
33 |
34 | /**
35 | * @param string $field
36 | * @param $value
37 | * @return QueryBuilderInterface
38 | */
39 | public function andWhere(string $field, $value): QueryBuilderInterface
40 | {
41 | $this->builder->where()->add($field, $value);
42 |
43 | $this->sql.=$this->builder->where()->getSQL();
44 |
45 | return $this;
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getSQL(): string
52 | {
53 | return trim($this->sql);
54 | }
55 |
56 |
57 | }
--------------------------------------------------------------------------------
/src/DB/Builder/WhereCollection.php:
--------------------------------------------------------------------------------
1 | whereClause[$field] = $value;
20 | return $this;
21 | }
22 |
23 | /**
24 | * @param array $where
25 | * @return WhereCollection
26 | */
27 | public function set(array $where = []): CollectionInterface
28 | {
29 | $this->whereClause = $where;
30 |
31 | return $this;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getSQL(): string
38 | {
39 | $sql = '';
40 | $counter = 0;
41 | $count = count($this->whereClause);
42 |
43 | if($count > 0) {
44 | $sql.='WHERE ';
45 | }
46 | foreach ($this->whereClause as $field => $value) {
47 | $counter++;
48 | $sql .= '`' . $field . '` =:' . $field;
49 |
50 | if ($counter < $count) {
51 | $sql .= ' AND ';
52 | }
53 |
54 | }
55 | return $sql;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/tests/acceptance/InvoicePageCest.php:
--------------------------------------------------------------------------------
1 | wantTo('Test the response code for the invoice dashboard page');
17 | $I->amOnPage('/invoice');
18 | $I->canSee('This is the invoice dashboard');
19 | $I->seeResponseCodeIs(200);
20 | }
21 |
22 | /**
23 | * @param AcceptanceTester $I
24 | *
25 | * @group invoice
26 | * @group invoice-get-by-id
27 | */
28 | public function invoiceTest(AcceptanceTester $I)
29 | {
30 | $I->wantTo('Test the response code for the invoice');
31 | $I->amOnPage('/invoice/123');
32 | $I->canSee('This is the invoice page for invoice 123 ');
33 | $I->seeResponseCodeIs(200);
34 | }
35 |
36 | /**
37 | * @param AcceptanceTester $I
38 | *
39 | * @group invoice
40 | * @group invoice-not-found
41 | */
42 | public function pageNotFoundTest(AcceptanceTester $I)
43 | {
44 | $I->wantTo('Test the response code for the invoice');
45 | $I->amOnPage('/invoice/rrr/edit/aaa');
46 | $I->seeResponseCodeIs(404);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Validation/Type/PatternValidatorTest.php:
--------------------------------------------------------------------------------
1 | setRoute($route);
33 |
34 | $this->assertFalse($validator->isValid());
35 | }
36 |
37 | /**
38 | * @group validation
39 | * @group validation-type
40 | * @group validation-type-pattern
41 | * @group validation-type-pattern-is-valid
42 | */
43 | public function testIsValid()
44 | {
45 | $route = new Route();
46 | $route->setPattern('/');
47 | $validator = new PatternValidator();
48 | $validator->setRoute($route);
49 |
50 | $this->assertTrue($validator->isValid());
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/tests/unit/Hydrator/InvoiceTest.php:
--------------------------------------------------------------------------------
1 | 1,
32 | 'reference' => 'invoice-001',
33 | 'total' => 2.99,
34 | 'vat' => 1.25,
35 | 'date_created' => $date->format(DateTime::ISO8601),
36 | 'date_updated' => $date->format(DateTime::ISO8601)
37 | ];
38 | $entity = InvoiceHydrator::hydrate($data);
39 |
40 | $this->assertInstanceOf(Invoice::class, $entity);
41 | $this->assertSame(1, $entity->getId());
42 | $this->assertSame('invoice-001', $entity->getReference());
43 | $this->assertSame(2.99, $entity->getTotal());
44 | $this->assertSame(1.25, $entity->getVAT());
45 | $this->assertInstanceOf(DateTime::class, $entity->getDateCreated());
46 | $this->assertInstanceOf(DateTime::class, $entity->getDateUpdated());
47 | }
48 | }
--------------------------------------------------------------------------------
/src/DB/Connection.php:
--------------------------------------------------------------------------------
1 | env = $env;
23 | }
24 |
25 | public function getCreds():array
26 | {
27 | return [
28 | 'username' => getenv( strtoupper($this->env) . '_MYSQL_USERNAME'),
29 | 'password' => getenv(strtoupper($this->env) . '_MYSQL_ROOT_PASSWORD'),
30 | 'host' => getenv(strtoupper($this->env) . '_MYSQL_HOST'),
31 | 'database' => getenv(strtoupper($this->env) . '_MYSQL_DATABASE')
32 | ];
33 | }
34 |
35 | /**
36 | * @return PDO
37 | */
38 | public function open():PDO
39 | {
40 | $creds = $this->getCreds();
41 |
42 | if(!self::$conn instanceof PDO) {
43 | $username = $creds['username'];
44 | $password = $creds['password'];
45 | $host = $creds['host'];
46 | $database = $creds['database'];
47 |
48 | $dsn = 'mysql:host=' . $host . ';dbname=' . $database;
49 |
50 | self::$conn = new PDO($dsn, $username, $password);
51 | }
52 | return self::$conn;
53 | }
54 | }
--------------------------------------------------------------------------------
/tests/unit/Hydrator/CustomerTest.php:
--------------------------------------------------------------------------------
1 | 1,
31 | 'company_name' => 'Acme Inc',
32 | 'first_name' => 'Peter',
33 | 'last_name' => 'Fisher',
34 | 'date_created' => $date->format(DateTime::ISO8601),
35 | 'date_updated' => $date->format(DateTime::ISO8601)
36 | ];
37 | $entity = CustomerHydrator::hydrate($data);
38 |
39 | $this->assertInstanceOf(Customer::class, $entity);
40 | $this->assertSame(1, $entity->getId());
41 | $this->assertSame('Acme Inc', $entity->getCompanyName());
42 | $this->assertSame('Peter', $entity->getFirstName());
43 | $this->assertSame('Fisher', $entity->getLastName());
44 | $this->assertInstanceOf(DateTime::class, $entity->getDateCreated());
45 | $this->assertInstanceOf(DateTime::class, $entity->getDateUpdated());
46 |
47 | }
48 | }
--------------------------------------------------------------------------------
/tests/unit/DB/BuilderTest.php:
--------------------------------------------------------------------------------
1 | builder = $builder;
30 | }
31 |
32 | /**
33 | * @group entity
34 | * @group db
35 | * @group db-query-builder
36 | * @group db-query-builder-where
37 | */
38 | public function testWhere()
39 | {
40 | $where = $this->builder->make(WhereCollection::class);
41 |
42 | $this->assertInstanceOf(WhereCollection::class, $where);
43 | }
44 |
45 | /**
46 | * @group entity
47 | * @group db
48 | * @group db-query-builder
49 | * @group db-query-builder-fields
50 | */
51 | public function testFields()
52 | {
53 | $where = $this->builder->make(FieldCollection::class);
54 |
55 | $this->assertInstanceOf(FieldCollection::class, $where);
56 | }
57 |
58 | protected function _before()
59 | {
60 | }
61 |
62 | protected function _after()
63 | {
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/src/Manager/StatusManager.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
23 | }
24 |
25 | /**
26 | * @param int $id
27 | *
28 | * @return null|Status
29 | */
30 | public function findOne(int $id): ?Status
31 | {
32 | $entity = $this->repository->findOne($id);
33 | return $entity;
34 | }
35 |
36 | /**
37 | * @inheritDoc
38 | */
39 | public function findAll(): array
40 | {
41 | $results = [];
42 | $rows = $this->repository->findAll('status');
43 |
44 | if (is_array($rows)) {
45 | foreach ($rows as $row) {
46 | $results[] = StatusHydrator::hydrate($row);
47 | }
48 | }
49 |
50 | return $results;
51 | }
52 |
53 | /**
54 | * @param Status $entity
55 | * @return Status
56 | */
57 | public function save(Status $entity)
58 | {
59 | $savedEntity = $this->repository->save($entity);
60 |
61 | return $savedEntity;
62 | }
63 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.3-apache
2 |
3 | LABEL maintainer="Peter Fisher"
4 |
5 | RUN apt-get update \
6 | && apt-get install -y \
7 | git \
8 | zlib1g-dev \
9 | zip \
10 | unzip \
11 | libxml2-dev \
12 | libgd-dev \
13 | libpng-dev \
14 | libfreetype6-dev \
15 | libjpeg62-turbo-dev \
16 | libzip-dev \
17 | && pecl install xdebug \
18 | && docker-php-ext-install mysqli pdo_mysql iconv simplexml \
19 | && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
20 | && docker-php-ext-configure zip --with-libzip \
21 | && docker-php-ext-install gd zip \
22 | && docker-php-ext-enable xdebug \
23 | && apt-get clean all \
24 | && rm -rvf /var/lib/apt/lists/* \
25 | && a2enmod rewrite headers
26 |
27 | RUN curl -sS https://getcomposer.org/installer | php -- --filename=composer --install-dir=/bin
28 | ENV PATH /root/.composer/vendor/bin:$PATH
29 |
30 | WORKDIR /var/www/html
--------------------------------------------------------------------------------
/src/Manager/InvoiceManager.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
23 | }
24 |
25 |
26 | /**
27 | * @param int $id
28 | *
29 | * @return Invoice|null
30 | */
31 | public function findOne(int $id): ?Invoice
32 | {
33 | $entity = $this->repository->findOne($id);
34 | return $entity;
35 | }
36 |
37 | /**
38 | * @inheritDoc
39 | */
40 | public function findAll(): array
41 | {
42 | $results = [];
43 | $rows = $this->repository->findAll('invoice');
44 | if (is_array($rows)) {
45 | foreach ($rows as $row) {
46 | $results[] = InvoiceHydrator::hydrate($row);
47 | }
48 | }
49 | return $results;
50 | }
51 |
52 | /**
53 | * @param Invoice $entity
54 | * @return Invoice
55 | */
56 | public function save(Invoice $entity): Invoice
57 | {
58 | $savedEntity = $this->repository->save($entity);
59 | return $savedEntity;
60 | }
61 | }
--------------------------------------------------------------------------------
/src/Manager/CustomerManager.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
23 | }
24 |
25 | /**
26 | * @param int $id
27 | *
28 | * @return null|Customer
29 | */
30 | public function findOne(int $id): ?Customer
31 | {
32 | $entity = $this->repository->findOne($id);
33 | return $entity;
34 | }
35 |
36 | /**
37 | * @inheritDoc
38 | */
39 | public function findAll(): array
40 | {
41 | $results = [];
42 | $rows = $this->repository->findAll('customer');
43 |
44 | if (is_array($rows)) {
45 | foreach ($rows as $row) {
46 | $results[] = CustomerHydrator::hydrate($row);
47 | }
48 | }
49 |
50 | return $results;
51 | }
52 |
53 | /**
54 | * @param Customer $entity
55 | * @return Customer
56 | */
57 | public function save(Customer $entity)
58 | {
59 | $savedEntity = $this->repository->save($entity);
60 |
61 | return $savedEntity;
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Validation/ValidationTest.php:
--------------------------------------------------------------------------------
1 | addValidator($validator);
34 |
35 | $this->assertTrue($validation->hasValidator(ControllerValidator::class));
36 |
37 | }
38 |
39 | /**
40 | * @group validation
41 | * @group validation-add-two-validator
42 | */
43 | public function testValidationAddTwo()
44 | {
45 | $validator1 = new ControllerValidator();
46 | $validator2 = new MethodValidator();
47 |
48 | $validation = new Validation();
49 | $validation->addValidator($validator1);
50 | $validation->addValidator($validator2);
51 |
52 | $this->assertTrue($validation->hasValidator(ControllerValidator::class));
53 | $this->assertTrue($validation->hasValidator(MethodValidator::class));
54 |
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/tests/unit/DB/StatusQueryBuilderTest.php:
--------------------------------------------------------------------------------
1 | builder = $builder;
25 | }
26 |
27 | /**
28 | * @group entity
29 | * @group db
30 | * @group db-query-builder-status
31 | * @group db-query-builder-status-select-all
32 | */
33 | public function testSelectAll()
34 | {
35 | $queryBuilder = new QueryBuilder($this->builder);
36 | $queryBuilder->select('status');
37 |
38 | $this->assertSame('SELECT * FROM `status`', $queryBuilder->getSQL());
39 | }
40 |
41 | /**
42 | * @group entity
43 | * @group db
44 | * @group db-query-builder-status
45 | * @group db-query-builder-status-select-by-id
46 | */
47 | public function testSelectByID()
48 | {
49 | $queryBuilder = new QueryBuilder($this->builder);
50 | $queryBuilder->select('status');
51 | $queryBuilder->andWhere('id', 4);
52 |
53 | $this->assertSame("SELECT * FROM `status` WHERE `id` =:id", $queryBuilder->getSQL());
54 | }
55 |
56 |
57 | protected function _before()
58 | {
59 | }
60 |
61 | protected function _after()
62 | {
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/notes.php:
--------------------------------------------------------------------------------
1 | getDoctrine()->getRepository(\App\Entity\Status::class)->findAll()
32 | * $this->repostioryFactory()->make(\App\Entity\Status::class)->findAll()
33 | *
34 | * $this->getDB()->getRepository(\App\Entity\Invoice::class)->findAll();
35 | *
36 | * $this->repositoryLoader->getStatus()->findAll();
37 | *
38 | * @todo Add abstraction layer for managers to allow controller to pull out
39 | * managers from a factory
40 | * $this->getDB()->getManager(\App\Entity\Status::class)->save($status);
41 | *
42 | *
43 | *
44 | */
45 |
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/URI/URIBuilderTest.php:
--------------------------------------------------------------------------------
1 | assertSame('#^$#', $uri);
33 | }
34 |
35 | /**
36 | * @group uri
37 | * @group uri-blank-default-with-array
38 | */
39 | public function testBlankDefaultWithArray()
40 | {
41 | $uri = URIBuilder::build('',['id' => 123]);
42 |
43 | $this->assertSame('#^$#', $uri);
44 | }
45 |
46 | /**
47 | * @group uri
48 | * @group uri-place-holder
49 | */
50 | public function testPlaceHolder()
51 | {
52 | $uri = URIBuilder::build('/{foo_bar}', ['foo_bar' => 'abc']);
53 |
54 | $this->assertSame('#^/abc$#', $uri);
55 | }
56 |
57 | /**
58 | * @group uri
59 | * @group uri-broken-place-holder
60 | */
61 | public function testBrokenPlaceHolder()
62 | {
63 | $uri = URIBuilder::build('/{foo_', ['foo' => 'abc']);
64 |
65 | $this->assertSame('#^/{foo_$#', $uri);
66 | }
67 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Validation/Validation.php:
--------------------------------------------------------------------------------
1 | validators[get_class($validator)] = $validator;
20 | }
21 | }
22 |
23 | /**
24 | * @param InterfaceValidator $interfaceValidation
25 | */
26 | public function addValidator(InterfaceValidator $interfaceValidation)
27 | {
28 | $this->setValidators([$interfaceValidation]);
29 | }
30 |
31 | /**
32 | * @param string $className
33 | *
34 | * @return bool
35 | */
36 | public function hasValidator($className):bool
37 | {
38 | return key_exists($className, $this->validators);
39 | }
40 |
41 | /**
42 | * @param Route $route
43 | *
44 | * @return bool
45 | */
46 | public function validate(Route $route):bool
47 | {
48 | $isValid = false;
49 |
50 | foreach($this->validators as $validator) {
51 | if($validator instanceof InterfaceValidator) {
52 | $validator->setRoute($route);
53 | $isValid = $validator->isValid();
54 |
55 | if(false === $isValid) {
56 | break;
57 | }
58 | }
59 | }
60 |
61 | return $isValid;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/Entity/AbstractEntity.php:
--------------------------------------------------------------------------------
1 | id;
29 | }
30 |
31 | /**
32 | * @param int|null $id
33 | * @return AbstractEntity
34 | */
35 | public function setId(?int $id): AbstractEntity
36 | {
37 | $this->id = $id;
38 | return $this;
39 | }
40 |
41 | /**
42 | * @return DateTime|null
43 | */
44 | public function getDateCreated(): ?DateTime
45 | {
46 | return $this->dateCreated;
47 | }
48 |
49 | /**
50 | * @param DateTime|null $dateCreated
51 | * @return AbstractEntity
52 | */
53 | public function setDateCreated(?DateTime $dateCreated): AbstractEntity
54 | {
55 | $this->dateCreated = $dateCreated;
56 | return $this;
57 | }
58 |
59 | /**
60 | * @return DateTime|null
61 | */
62 | public function getDateUpdated(): ?DateTime
63 | {
64 | return $this->dateUpdated;
65 | }
66 |
67 | /**
68 | * @param DateTime|null $dateUpdated
69 | * @return AbstractEntity
70 | */
71 | public function setDateUpdated(?DateTime $dateUpdated): AbstractEntity
72 | {
73 | $this->dateUpdated = $dateUpdated;
74 | return $this;
75 | }
76 |
77 |
78 | }
--------------------------------------------------------------------------------
/tests/unit/Hydrator/InvoiceItemTest.php:
--------------------------------------------------------------------------------
1 | 1,
31 | 'reference' => 'invoice-item-001',
32 | 'description' => 'Development time',
33 | 'unit_price' => 2.85,
34 | 'units' => 7,
35 | 'total' => 350,
36 | 'date_created' => $date->format(DateTime::ISO8601),
37 | 'date_updated' => $date->format(DateTime::ISO8601)
38 | ];
39 | $entity = InvoiceItemHydrator::hydrate($data);
40 |
41 | $this->assertInstanceOf(InvoiceItem::class, $entity);
42 | $this->assertSame(1, $entity->getId());
43 | $this->assertSame('invoice-item-001', $entity->getReference());
44 | $this->assertSame('Development time', $entity->getDescription());
45 | $this->assertSame(2.85, $entity->getUnitPrice());
46 | $this->assertSame(7, $entity->getUnits());
47 | $this->assertSame(350.0, $entity->getTotal());
48 | $this->assertInstanceOf(DateTime::class, $entity->getDateCreated());
49 | $this->assertInstanceOf(DateTime::class, $entity->getDateUpdated());
50 | }
51 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Route/RouteTest.php:
--------------------------------------------------------------------------------
1 | assertSame('', $route->getController());
31 | $this->assertSame('', $route->getAction());
32 | $this->assertSame('', $route->getPattern());
33 | $this->assertSame([], $route->getMethods());
34 |
35 | }
36 |
37 | /**
38 | * @group route
39 | * @group route-home-page
40 | */
41 | public function testHomePageIndex()
42 | {
43 | $route = new Route();
44 | $route->setController(Home::class)
45 | ->setMethods(['GET'])
46 | ->setPattern('/')
47 | ->setAction('index')
48 | ;
49 |
50 | $this->assertSame(Home::class, $route->getController());
51 | $this->assertSame(['GET'], $route->getMethods());
52 | $this->assertSame('/', $route->getPattern());
53 | $this->assertSame('index', $route->getAction());
54 |
55 | }
56 |
57 | /**
58 | * @group route
59 | * @group route-upper-case-method
60 | */
61 | public function testUpperCaseMethod()
62 | {
63 | $route = new Route();
64 | $route->setMethods(['get']);
65 |
66 | $this->assertSame(['GET'], $route->getMethods());
67 |
68 | }
69 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Request/Factory/Type/ServerFactoryTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Request::class, $request);
36 | }
37 |
38 | /**
39 | * @group request
40 | * @group request-server-factory-uri-exception
41 | */
42 | public function testURIException()
43 | {
44 | $this->tester->expectException(new Exception("REQUEST_URI not found"), function() {
45 | $_SERVER['REQUEST_METHOD'] = 'GET';
46 |
47 | $factory = new ServerFactory();
48 | $factory::make();
49 | });
50 |
51 | }
52 |
53 | /**
54 | * @group request
55 | * @group request-server-factory-method-exception
56 | */
57 | public function testMethodException()
58 | {
59 | $this->tester->expectException(new Exception("REQUEST_METHOD not found"), function() {
60 | $_SERVER['REQUEST_URI'] = '/';
61 |
62 | $factory = new ServerFactory();
63 | $factory::make();
64 | });
65 |
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/src/Manager/InvoiceItemManager.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
23 | }
24 |
25 | /**
26 | * @param int $id
27 | *
28 | * @return null|InvoiceItem
29 | */
30 | public function findOne(int $id): ?InvoiceItem
31 | {
32 | $entity = $this->repository->findOne($id);
33 | return $entity;
34 | }
35 |
36 | /**
37 | * @inheritDoc
38 | */
39 | public function findAll(): array
40 | {
41 | $results = [];
42 | $rows = $this->repository->findAll('invoice_item');
43 |
44 | if (is_array($rows)) {
45 | foreach ($rows as $row) {
46 | $results[] = InvoiceItemHydrator::hydrate($row);
47 | }
48 | }
49 |
50 | return $results;
51 | }
52 |
53 | /**
54 | * @param int $id
55 | * @return array
56 | */
57 | public function findAllByInvoiceID(int $id): array
58 | {
59 | return $this->repository->findAllByInvoiceID($id);
60 | }
61 |
62 | /**
63 | * @param InvoiceItem $entity
64 | * @return InvoiceItem
65 | */
66 | public function save(InvoiceItem $entity)
67 | {
68 | $savedEntity = $this->repository->save($entity);
69 |
70 | return $savedEntity;
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/src/Repository/RepositoryFactory.php:
--------------------------------------------------------------------------------
1 | connection = $connection;
32 | $this->queryBuilder = $queryBuilder;
33 | }
34 |
35 | /**
36 | * @param string $entityClassName
37 | * @return mixed
38 | */
39 | public function make(string $entityClassName): RepositoryInterface
40 | {
41 | if (key_exists($entityClassName, $this->repositories)) {
42 | return $this->repositories[$entityClassName];
43 | }
44 |
45 | $map = [
46 | Status::class => StatusRepository::class,
47 | Invoice::class => InvoiceRepository::class
48 | ];
49 |
50 | if(key_exists($entityClassName, $map)) {
51 | $repo = $map[$entityClassName];
52 | }
53 |
54 |
55 | return $this->repositories[$entityClassName] = new $repo($this->connection, $this->queryBuilder);
56 | }
57 | }
58 |
59 | $connection = new Connection();
60 | $builder = new Builder();
61 | $queryBuilder = new QueryBuilder($builder);
62 | $respositoryFactory = new RepositoryFactory($connection, $queryBuilder);
63 |
64 | $statusRepo = $respositoryFactory->make(Status::class)->findAll();
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FreeCodeCamp PHP OOP Course
2 | ### By Peter Fisher How To Code Well
3 |
4 | - [Installation](#installation)
5 | - [Database](#database)
6 | - [Requirements](#requirements)
7 | - [Testing](#testing)
8 |
9 | ## Installation
10 | Create `.env`
11 | ```
12 | $ cp .env.dist .env
13 | ```
14 | Modify values in `.env`
15 |
16 | Creating Docker Machine (Optional)
17 | ```
18 | $ docker-machine create howtocodewell-oop-php
19 | $ docker-machine env howtocodewell-oop-php
20 | $ eval $(docker-machine env howtocodewell-oop-php)
21 | ```
22 |
23 | Create the containers and build the images
24 | ```
25 | $ docker-compose up -d --build
26 | ```
27 |
28 | Find IP of Docker machine
29 | ```
30 | $ docker-machine ip howtocodewell-oop-php
31 | 192.168.99.100
32 | ```
33 |
34 | Put the IP in a browser
35 |
36 | ### Database
37 | *Please note: This will delete the database and create a new one. All data will be lost*
38 |
39 | To rebuild the database run the following command from the host machine. (Change )
40 | ```
41 | $ docker-compose exec -T db mysql -u root --password= < mysql/rebuild.sql
42 | ```
43 | Or from within the container
44 | ```
45 | $ docker-compose exec db mysql -u root -p
46 | Enter password:
47 |
48 | mysql> use invoice_app;
49 | mysql> source /scripts/rebuild.sql
50 | ```
51 | ## Requirements
52 | - Docker 18.09.2
53 | - Docker Machine 0.16.1 (Optional)
54 | - Docker Compose 1.23.2
55 |
56 | ## Testing
57 | Run unit tests
58 | ```
59 | $ docker-compose exec web vendor/bin/codecept run unit
60 | ```
61 |
62 | Run unit tests with code coverage
63 | ```
64 | $ docker-compose exec web vendor/bin/codecept run unit --coverage --coverage-xml --coverage-html
65 | $ open tests/_output/coverage/index.html
66 | ```
67 |
68 | Run acceptance tests
69 | ```
70 | $ docker-compose exec web vendor/bin/codecept run acceptance
71 | ```
--------------------------------------------------------------------------------
/src/Repository/CustomerRepository.php:
--------------------------------------------------------------------------------
1 | $entity->getFirstName(),
21 | 'last_name' => $entity->getLastName(),
22 | 'company_name' => $entity->getCompanyName(),
23 | ];
24 | $table = 'customer';
25 | $where = [];
26 | if (null !== $entity->getId()) {
27 | $where['id'] = $entity->getId();
28 | }
29 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
30 |
31 | if (null !== $entity->getId()) {
32 | $data['id'] = $entity->getId();
33 | }
34 |
35 | $dbCon = $this->connection->open();
36 |
37 | $statement = $dbCon->prepare($sql);
38 | $statement->execute($data);
39 |
40 | if (null === $entity->getId()) {
41 | $entity->setId((int)$dbCon->lastInsertId());
42 | }
43 |
44 | return $entity;
45 | }
46 |
47 | /**
48 | * @param int $id
49 | * @return mixed
50 | */
51 | public function findOne(int $id)
52 | {
53 | $entity = null;
54 | $sql = QueryBuilder::findOneBy('customer');
55 | $dbCon = $this->connection->open();
56 |
57 | $statement = $dbCon->prepare($sql);
58 | $statement->execute([
59 | 'id' => $id
60 | ]);
61 | $row = $statement->fetch();
62 |
63 | if ($row) {
64 | $entity = CustomerHydrator::hydrate($row);
65 | }
66 | return $entity;
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/tests/unit/DB/FieldCollectionTest.php:
--------------------------------------------------------------------------------
1 | add('id');
28 |
29 | $this->assertSame('`id`', $fieldCollection->getSQL());
30 | }
31 |
32 | /**
33 | * @group entity
34 | * @group db
35 | * @group db-field-collection
36 | * @group db-field-collection-add-multiple
37 | */
38 | public function testAddMultiple()
39 | {
40 | $fieldCollection = new FieldCollection();
41 | $fieldCollection->add('id');
42 | $fieldCollection->add('total');
43 |
44 | $this->assertSame('`id`, `total`', $fieldCollection->getSQL());
45 | }
46 |
47 | /**
48 | * @group entity
49 | * @group db
50 | * @group db-field-collection
51 | * @group db-field-collection-set
52 | */
53 | public function testSet()
54 | {
55 | $fieldCollection = new FieldCollection();
56 | $fieldCollection->set(['id', 'total']);
57 |
58 | $this->assertSame('`id`, `total`', $fieldCollection->getSQL());
59 | }
60 |
61 | /**
62 | * @group entity
63 | * @group db
64 | * @group db-field-collection
65 | * @group db-field-collection-all
66 | */
67 | public function testAll()
68 | {
69 | $fieldCollection = new FieldCollection();
70 |
71 | $this->assertSame('*', $fieldCollection->getSQL());
72 | }
73 |
74 | protected function _before()
75 | {
76 | }
77 |
78 | protected function _after()
79 | {
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/src/Repository/InvoiceRepository.php:
--------------------------------------------------------------------------------
1 | connection->open();
22 |
23 | $statement = $dbCon->prepare($sql);
24 | $statement->execute([
25 | 'id' => $id
26 | ]);
27 | $row = $statement->fetch();
28 |
29 | if ($row) {
30 | $entity = InvoiceHydrator::hydrate($row);
31 | }
32 | return $entity;
33 | }
34 |
35 | /**
36 | * @param Invoice $entity
37 | * @return Invoice
38 | */
39 | public function save(Invoice $entity): Invoice
40 | {
41 | $data = [
42 | 'reference' => $entity->getReference(),
43 | 'total' => $entity->getTotal(),
44 | 'vat' => $entity->getVat()
45 | ];
46 | if ($entity->getStatus() instanceof Status) {
47 | $data['status_id'] = $entity->getStatus()->getId();
48 | }
49 | $where = [];
50 | if (null !== $entity->getId()) {
51 | $where['id'] = $entity->getId();
52 | }
53 |
54 | $table = 'invoice';
55 |
56 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
57 |
58 | if (null !== $entity->getId()) {
59 | $data['id'] = $entity->getId();
60 | }
61 | $dbCon = $this->connection->open();
62 |
63 | $statement = $dbCon->prepare($sql);
64 | $statement->execute($data);
65 |
66 | if (null === $entity->getId()) {
67 | $entity->setId($dbCon->lastInsertId());
68 | }
69 |
70 | return $entity;
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/src/Entity/Customer.php:
--------------------------------------------------------------------------------
1 | firstName)) {
29 | $names[] = $this->firstName;
30 | }
31 |
32 | if(false === empty($this->lastName)) {
33 | $names[] = $this->lastName;
34 | }
35 |
36 | return implode(' ', $names);
37 | }
38 |
39 | /**
40 | * @return string
41 | */
42 | public function getCompanyName(): string
43 | {
44 | return $this->companyName;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function getFirstName(): string
51 | {
52 | return $this->firstName;
53 | }
54 |
55 | /**
56 | * @return string
57 | */
58 | public function getLastName(): string
59 | {
60 | return $this->lastName;
61 | }
62 |
63 | /**
64 | * @param string $companyName
65 | * @return Customer
66 | */
67 | public function setCompanyName(string $companyName): Customer
68 | {
69 | $this->companyName = $companyName;
70 | return $this;
71 | }
72 |
73 | /**
74 | * @param string $firstName
75 | * @return Customer
76 | */
77 | public function setFirstName(string $firstName): Customer
78 | {
79 | $this->firstName = $firstName;
80 | return $this;
81 | }
82 |
83 | /**
84 | * @param string $lastName
85 | * @return Customer
86 | */
87 | public function setLastName(string $lastName): Customer
88 | {
89 | $this->lastName = $lastName;
90 | return $this;
91 | }
92 |
93 |
94 | }
--------------------------------------------------------------------------------
/tests/unit/Entity/StatusTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty($status->getName());
30 | $this->assertIsString($status->getName());
31 | $this->assertEmpty($status->getInternalName());
32 | $this->assertIsString($status->getInternalName());
33 | }
34 |
35 | /**
36 | * @group entity
37 | * @group status
38 | * @group status-set-name
39 | */
40 | public function testSetName()
41 | {
42 | $status = new Status();
43 | $status->setName('draft');
44 |
45 | $this->assertSame('draft', $status->getName());
46 | $this->assertSame('DRAFT', $status->getInternalName());
47 | $this->assertInstanceOf(Status::class, $status);
48 | }
49 |
50 | /**
51 | * @group entity
52 | * @group status
53 | * @group status-set-internal-name
54 | */
55 | public function testSetInternalName()
56 | {
57 | $status = new Status();
58 | $status->setInternalName('draft');
59 |
60 | $this->assertSame('DRAFT', $status->getInternalName());
61 | $this->assertInstanceOf(Status::class, $status);
62 | }
63 |
64 | /**
65 | * @group entity
66 | * @group status
67 | * @group status-set-internal-name-with-spaces
68 | */
69 | public function testSetInternalNameWithSpaces()
70 | {
71 | $status = new Status();
72 | $status->setInternalName('Under Review');
73 |
74 | $this->assertSame('UNDER_REVIEW', $status->getInternalName());
75 | $this->assertInstanceOf(Status::class, $status);
76 | }
77 |
78 |
79 | }
--------------------------------------------------------------------------------
/tests/unit/DB/WhereCollectionTest.php:
--------------------------------------------------------------------------------
1 | add('id', 10);
26 |
27 | $this->assertSame('WHERE `id` =:id', $whereCollection->getSQL());
28 | }
29 |
30 | /**
31 | * @group entity
32 | * @group db
33 | * @group db-where-collection
34 | * @group db-where-collection-add-multiple
35 | */
36 | public function testAddMultiple()
37 | {
38 | $whereCollection = new WhereCollection();
39 | $whereCollection->add('id', 10);
40 | $whereCollection->add('total', 15);
41 |
42 | $this->assertSame('WHERE `id` =:id AND `total` =:total', $whereCollection->getSQL());
43 | }
44 |
45 | /**
46 | * @group entity
47 | * @group db
48 | * @group db-where-collection
49 | * @group db-where-collection-set
50 | */
51 | public function testSet()
52 | {
53 | $whereCollection = new WhereCollection();
54 | $whereCollection->set([
55 | 'id' => 19,
56 | 'total' => 3
57 | ]);
58 |
59 | $this->assertSame('WHERE `id` =:id AND `total` =:total', $whereCollection->getSQL());
60 | }
61 |
62 | /**
63 | * @group entity
64 | * @group db
65 | * @group db-where-collection
66 | * @group db-where-collection-blank
67 | */
68 | public function testBlank()
69 | {
70 | $whereCollection = new WhereCollection();
71 |
72 | $this->assertSame('', $whereCollection->getSQL());
73 | }
74 |
75 | protected function _before()
76 | {
77 | }
78 |
79 | protected function _after()
80 | {
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Route/RouterTest.php:
--------------------------------------------------------------------------------
1 | assertIsArray($router->getRoutes());
31 | $this->assertEquals(0, count($router->getRoutes()));
32 | }
33 |
34 | /**
35 | * @group router
36 | * @group router-routes-array
37 | */
38 | public function testIsRoutesAnArray()
39 | {
40 | $router = new Router();
41 | $this->assertIsArray($router->getRoutes());
42 | }
43 |
44 | /**
45 | * @group router
46 | * @group router-add-route
47 | */
48 | public function testAddRoute()
49 | {
50 | $route = new Route();
51 | $route->setController(Home::class)
52 | ->setMethods(['GET'])
53 | ->setPattern('/')
54 | ->setAction('index')
55 | ;
56 |
57 | $router = new Router();
58 | $router::add($route);
59 |
60 | $this->assertIsArray($router->getRoutes());
61 | $this->assertEquals(1, count($router->getRoutes()));
62 | }
63 |
64 | /**
65 | * @group router
66 | * @group router-add-duplicate
67 | */
68 | public function testAddDuplicateRoute()
69 | {
70 | $route = new Route();
71 | $route->setController(Home::class)
72 | ->setMethods(['GET'])
73 | ->setPattern('/')
74 | ->setAction('index')
75 | ;
76 |
77 | $router = new Router();
78 | $router::add($route);
79 | $router::add($route);
80 |
81 | $this->assertIsArray($router->getRoutes());
82 | $this->assertEquals(1, count($router->getRoutes()));
83 | }
84 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Validation/Type/ActionValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($validator->doesExist(Home::class, 'index'));
33 | }
34 |
35 | /**
36 | * @group validation
37 | * @group validation-type
38 | * @group validation-type-action
39 | * @group validation-type-action-does-not-exist
40 | */
41 | public function testDoesNotActionExist()
42 | {
43 | $validator = new ActionValidator();
44 | $this->assertFalse($validator->doesExist(Home::class, 'NOT_FOUND'));
45 | }
46 |
47 | /**
48 | * @group validation
49 | * @group validation-type
50 | * @group validation-type-action
51 | * @group validation-type-action-is-in-valid
52 | */
53 | public function testIsInValid()
54 | {
55 | $route = new Route();
56 |
57 | $validator = new ActionValidator();
58 | $validator->setRoute($route);
59 | $this->assertFalse($validator->isValid());
60 | }
61 |
62 | /**
63 | * @group validation
64 | * @group validation-type
65 | * @group validation-type-action
66 | * @group validation-type-action-is-valid
67 | */
68 | public function testIsValid()
69 | {
70 | $route = new Route();
71 | $route->setController(Home::class);
72 | $route->setAction('index');
73 |
74 | $validator = new ActionValidator();
75 | $validator->setRoute($route);
76 | $this->assertTrue($validator->isValid());
77 | }
78 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Validation/Type/ControllerValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($validator->doesExist(Home::class));
34 | }
35 |
36 | /**
37 | * @group validation
38 | * @group validation-type
39 | * @group validation-type-controller
40 | * @group validation-type-controller-does-not-exist
41 | */
42 | public function testDoesNotControllerExist()
43 | {
44 | $validator = new ControllerValidator();
45 | $this->assertFalse($validator->doesExist('NOT_FOUND'));
46 | }
47 |
48 | /**
49 | * @group validation
50 | * @group validation-type
51 | * @group validation-type-controller
52 | * @group validation-type-controller-empty
53 | */
54 | public function testDoesNotControllerEmpty()
55 | {
56 | $route = new Route();
57 |
58 | $validator = new ControllerValidator();
59 | $validator->setRoute($route);
60 | $this->assertFalse($validator->isValid());
61 | }
62 |
63 | /**
64 | * @group validation
65 | * @group validation-type
66 | * @group validation-type-controller
67 | * @group validation-type-controller-set
68 | */
69 | public function testDoesNotControllerSet()
70 | {
71 | $route = new Route();
72 | $route->setController(Home::class);
73 |
74 | $validator = new ControllerValidator();
75 | $validator->setRoute($route);
76 | $this->assertTrue($validator->isValid());
77 | }
78 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Validation/Type/MethodValidatorTest.php:
--------------------------------------------------------------------------------
1 | setController(Home::class)
35 | ->setMethods(['GET'])
36 | ;
37 |
38 | $validator = new MethodValidator();
39 | $validator->setRoute($route);
40 |
41 | $this->assertTrue($validator->isValid());
42 | }
43 |
44 | /**
45 | * @group validation
46 | * @group validation-type
47 | * @group validation-type-method
48 | * @group validation-type-method-is-lowecase
49 | */
50 | public function testIsLowerCase()
51 | {
52 | $route = new Route();
53 | $route->setController(Home::class)
54 | ->setMethods(['get'])
55 | ;
56 |
57 | $validator = new MethodValidator();
58 | $validator->setRoute($route);
59 |
60 | $this->assertTrue($validator->isValid());
61 | }
62 |
63 | /**
64 | * @group validation
65 | * @group validation-type
66 | * @group validation-type-method
67 | * @group validation-type-method-is-invalid
68 | */
69 | public function testIsInvalid()
70 | {
71 | $route = new Route();
72 | $route->setController(Home::class)
73 | ->setMethods(['this-should-not-work'])
74 | ;
75 |
76 | $validator = new MethodValidator();
77 | $validator->setRoute($route);
78 |
79 | $this->assertFalse($validator->isValid());
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/tests/unit/Entity/InvoiceTest.php:
--------------------------------------------------------------------------------
1 | assertNull($invoice->getCustomer());
30 | $this->assertIsArray($invoice->getItems());
31 | $this->assertEmpty($invoice->getItems());
32 | $this->assertIsString($invoice->getReference());
33 | $this->assertEmpty($invoice->getReference());
34 | $this->assertIsFloat($invoice->getTotal());
35 | $this->assertSame(0.0, $invoice->getTotal());
36 | $this->assertIsFloat($invoice->getVAT());
37 | $this->assertSame(0.0, $invoice->getVAT());
38 | $this->assertNull($invoice->getStatus());
39 | $this->assertNull($invoice->getId());
40 | $this->assertNull($invoice->getDateCreated());
41 | $this->assertNull($invoice->getDateUpdated());
42 | }
43 |
44 | /**
45 | * @group entity
46 | * @group invoice
47 | * @group invoice-set-reference
48 | */
49 | public function testSetReference()
50 | {
51 | $invoice = new Invoice();
52 | $invoice->setReference('abc123');
53 | $this->assertSame('abc123', $invoice->getReference());
54 | }
55 |
56 | /**
57 | * @group entity
58 | * @group invoice
59 | * @group invoice-set-total
60 | */
61 | public function testSetTotal()
62 | {
63 | $invoice = new Invoice();
64 | $invoice->setTotal(100.5);
65 | $this->assertSame( 100.5, $invoice->getTotal());
66 | }
67 |
68 | /**
69 | * @group entity
70 | * @group invoice
71 | * @group invoice-set-vat
72 | */
73 | public function testSetVAT()
74 | {
75 | $invoice = new Invoice();
76 | $invoice->setVAT(123.5);
77 | $this->assertSame( 123.5, $invoice->getVAT());
78 | }
79 |
80 |
81 | }
--------------------------------------------------------------------------------
/tests/acceptance/DBStatusEntityCest.php:
--------------------------------------------------------------------------------
1 | setName('Hello World2')
25 | ->setInternalName('HELLO_WORLD_2');
26 |
27 | $manager = $this->getManager();
28 | $manager->save($entity);
29 |
30 | $I->seeInDatabase('status', [
31 | 'name' => $entity->getName(),
32 | 'internal_name' => $entity->getInternalName()
33 | ]);
34 | }
35 |
36 | /**
37 | * @return StatusManager
38 | */
39 | public function getManager(): StatusManager
40 | {
41 | $connection = new Connection();
42 | $builder = new Builder();
43 | $queryBuilder = new QueryBuilder($builder);
44 | $repository = new StatusRepository($connection, $queryBuilder);
45 | return new StatusManager($repository);
46 | }
47 |
48 | /**
49 | * @param AcceptanceTester $I
50 | * @group db
51 | * @group db-status-entity-update
52 | */
53 | public function updateTest(AcceptanceTester $I)
54 | {
55 | $entity = new Status();
56 | $entity->setName('Test Status')
57 | ->setInternalName('TEST_STATUS');
58 | $manager = $this->getManager();
59 | $savedEntity = $manager->save($entity);
60 |
61 | $I->seeInDatabase('status', [
62 | 'name' => $entity->getName(),
63 | 'internal_name' => $entity->getInternalName(),
64 | 'id' => $savedEntity->getId()
65 | ]);
66 |
67 | $savedEntity->setName('Test Status 2');
68 | $savedEntity->setInternalName('TEST');
69 | $manager->save($savedEntity);
70 |
71 | $I->seeInDatabase('status', [
72 | 'name' => $savedEntity->getName(),
73 | 'id' => $savedEntity->getId()
74 | ]);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/tests/acceptance/DBCustomerEntityCest.php:
--------------------------------------------------------------------------------
1 | setFirstName('Test First Name')
36 | ->setLastName('Test Last Name')
37 | ->setCompanyName('Test Company Name');
38 |
39 | $this->getManager()->save($entity);
40 |
41 | $I->seeInDatabase('customer', [
42 | 'first_name' => $entity->getFirstName(),
43 | 'last_name' => $entity->getLastName(),
44 | 'company_name' => $entity->getCompanyName()
45 | ]);
46 | }
47 |
48 | /**
49 | * @param AcceptanceTester $I
50 | * @group db
51 | * @group db-customer-entity-update
52 | */
53 | public function updateTest(AcceptanceTester $I)
54 | {
55 | $entity = new Customer();
56 | $entity->setFirstName('Test First Name')
57 | ->setLastName('Test Last Name')
58 | ->setCompanyName('Test Company Name');
59 | $savedEntity = $this->getManager()->save($entity);
60 |
61 | $I->seeInDatabase('customer', [
62 | 'first_name' => $entity->getFirstName(),
63 | 'last_name' => $entity->getLastName(),
64 | 'company_name' => $entity->getCompanyName(),
65 | 'id' => $savedEntity->getId()
66 | ]);
67 |
68 | $savedEntity->setCompanyName('Test Company Name 2');
69 | $savedEntity = $this->getManager()->save($savedEntity);
70 |
71 | $I->seeInDatabase('customer', [
72 | 'company_name' => $savedEntity->getCompanyName(),
73 | 'id' => $savedEntity->getId()
74 | ]);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/unit/Entity/CustomerTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty($customer->getCompanyName());
30 | $this->assertIsString($customer->getCompanyName());
31 | $this->assertEmpty($customer->getFirstName());
32 | $this->assertIsString($customer->getFirstName());
33 | $this->assertEmpty($customer->getLastName());
34 | $this->assertIsString($customer->getLastName());
35 | $this->assertEmpty((string) $customer);
36 | }
37 |
38 | /**
39 | * @group entity
40 | * @group customer
41 | * @group customer-set-company-name
42 | */
43 | public function testSetCompanyName()
44 | {
45 | $customer = new Customer();
46 | $customer->setCompanyName('How To Code Well');
47 | $this->assertSame('How To Code Well', $customer->getCompanyName());
48 | }
49 |
50 | /**
51 | * @group entity
52 | * @group customer
53 | * @group customer-set-first-name
54 | */
55 | public function testSetFirstName()
56 | {
57 | $customer = new Customer();
58 | $customer->setFirstName('Peter');
59 | $this->assertSame('Peter', $customer->getFirstName());
60 | $this->assertSame('Peter', (string) $customer);
61 | }
62 |
63 | /**
64 | * @group entity
65 | * @group customer
66 | * @group customer-set-last-name
67 | */
68 | public function testSetLastName()
69 | {
70 | $customer = new Customer();
71 | $customer->setLastName('Fisher');
72 | $this->assertSame('Fisher', $customer->getLastName());
73 | $this->assertSame('Fisher', (string) $customer);
74 | }
75 |
76 | /**
77 | * @group entity
78 | * @group customer
79 | * @group customer-to-string
80 | */
81 | public function testFullName()
82 | {
83 | $customer = new Customer();
84 | $customer->setFirstName('Peter');
85 | $customer->setLastName('Fisher');
86 | $this->assertSame('Peter Fisher', (string) $customer);
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Route/Route.php:
--------------------------------------------------------------------------------
1 | pattern;
34 | }
35 |
36 | /**
37 | * @param string $pattern
38 | * @return Route
39 | */
40 | public function setPattern(string $pattern): Route
41 | {
42 | $this->pattern = $pattern;
43 | return $this;
44 | }
45 |
46 | /**
47 | * @return string
48 | */
49 | public function getController(): string
50 | {
51 | return $this->controller;
52 | }
53 |
54 | /**
55 | * @param string $controller
56 | * @return Route
57 | */
58 | public function setController(string $controller): Route
59 | {
60 | $this->controller = $controller;
61 | return $this;
62 | }
63 |
64 | /**
65 | * @return array
66 | */
67 | public function getMethods(): array
68 | {
69 | return $this->methods;
70 | }
71 |
72 | /**
73 | * @param array $methods
74 | * @return Route
75 | */
76 | public function setMethods(array $methods): Route
77 | {
78 | $this->methods = array_map('strtoupper', $methods);
79 |
80 | return $this;
81 | }
82 |
83 | /**
84 | * @return string
85 | */
86 | public function getAction(): string
87 | {
88 | return $this->action;
89 | }
90 |
91 | /**
92 | * @param string $action
93 | * @return Route
94 | */
95 | public function setAction(string $action): Route
96 | {
97 | $this->action = $action;
98 | return $this;
99 | }
100 |
101 | /**
102 | * @return array
103 | */
104 | public function getParameters(): array
105 | {
106 | return $this->parameters;
107 | }
108 |
109 | /**
110 | * @param array $parameters
111 | * @return Route
112 | */
113 | public function setParameters(array $parameters): Route
114 | {
115 | $this->parameters = $parameters;
116 | return $this;
117 | }
118 |
119 |
120 | }
--------------------------------------------------------------------------------
/src/Repository/InvoiceItemRepository.php:
--------------------------------------------------------------------------------
1 | connection->open();
21 |
22 | $statement = $dbCon->prepare($sql);
23 | $statement->execute([
24 | 'id' => $id
25 | ]);
26 | $row = $statement->fetch();
27 |
28 | if ($row) {
29 | $entity = InvoiceItemHydrator::hydrate($row);
30 | }
31 | return $entity;
32 | }
33 |
34 | /**
35 | * @param int $id
36 | * @return array
37 | */
38 | public function findAllByInvoiceID(int $id):array
39 | {
40 | $results = [];
41 | $data = [
42 | 'invoice_id' => $id
43 | ];
44 | $sql = QueryBuilder::findAllBy('invoice_item', $data);
45 |
46 | $dbCon = $this->connection->open();
47 | $statement = $dbCon->prepare($sql);
48 | $statement->execute($data);
49 | $rows = $statement->fetchAll();
50 |
51 | if (is_array($rows)) {
52 | foreach ($rows as $row) {
53 | $results[] = InvoiceItemHydrator::hydrate($row);
54 | }
55 | }
56 | return $results;
57 | }
58 |
59 | /**
60 | * @param InvoiceItem $entity
61 | * @return InvoiceItem
62 | */
63 | public function save(InvoiceItem $entity)
64 | {
65 |
66 | $data = [
67 | 'reference' => $entity->getReference(),
68 | 'total' => $entity->getTotal(),
69 | 'unit_price' => $entity->getUnitPrice(),
70 | 'units' => $entity->getUnits(),
71 | 'description' => $entity->getDescription(),
72 | 'invoice_id' => $entity->getInvoice()->getId()
73 | ];
74 | $table = 'invoice_item';
75 | $where = [];
76 | if (null !== $entity->getId()) {
77 | $where['id'] = $entity->getId();
78 | }
79 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
80 |
81 | if (null !== $entity->getId()) {
82 | $data['id'] = $entity->getId();
83 | }
84 |
85 | $dbCon = $this->connection->open();
86 |
87 | $statement = $dbCon->prepare($sql);
88 | $statement->execute($data);
89 |
90 | if (null === $entity->getId()) {
91 | $entity->setId((int)$dbCon->lastInsertId());
92 | }
93 |
94 | return $entity;
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/tests/integration/DBStatusEntityTest.php:
--------------------------------------------------------------------------------
1 | setName('Hello World2')
26 | ->setInternalName('HELLO_WORLD_2');
27 |
28 | $manager = $this->getManager();
29 | $savedEntity = $manager->save($entity);
30 |
31 | $foundEntity = $manager->findOne($savedEntity->getId());
32 |
33 | $this->assertSame($foundEntity->getName(), $savedEntity->getName());
34 | $this->assertSame($foundEntity->getInternalName(), $savedEntity->getInternalName());
35 | }
36 |
37 | /**
38 | * @return StatusManager
39 | */
40 | protected function getManager(): StatusManager
41 | {
42 | $connection = new Connection();
43 | $builder = new Builder();
44 | $queryBuilder = new QueryBuilder($builder);
45 | $repository = new StatusRepository($connection, $queryBuilder);
46 | return new StatusManager($repository);
47 | }
48 |
49 | /**
50 | * @group db-status
51 | * @group db-status-entity-find-one-by-id-not-exists
52 | */
53 | public function testFindStatusThatDoesNotExist()
54 | {
55 | $entity = new Status();
56 | $entity->setId(5001);
57 | $manager = $this->getManager();
58 |
59 | $foundEntity = $manager->findOne($entity->getId());
60 | $this->assertNull($foundEntity);
61 |
62 | }
63 |
64 | /**
65 | * @group db-status
66 | * @group db-status-entity-find-all-statuses
67 | */
68 | public function testFindAllStatuses()
69 | {
70 | $entity1 = new Status();
71 | $entity1->setName('Test 1');
72 | $entity1->setInternalName('TEST_1');
73 |
74 | $entity2 = new Status();
75 | $entity2->setName('Test 2');
76 | $entity2->setInternalName('TEST_2');
77 |
78 | $manager = $this->getManager();
79 |
80 | $manager->save($entity1);
81 | $manager->save($entity2);
82 |
83 | $results = $manager->findAll();
84 | $this->assertIsArray($results);
85 | $this->assertGreaterThan(1, count($results));
86 |
87 | $foundEntity1 = $results[0];
88 | $this->assertInstanceOf(Status::class, $foundEntity1);
89 |
90 | }
91 |
92 | protected function _before()
93 | {
94 | }
95 |
96 | protected function _after()
97 | {
98 | }
99 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Locator/Locator.php:
--------------------------------------------------------------------------------
1 | routes = $routes;
29 |
30 | if(null === $request) {
31 | $request = new Request();
32 | }
33 |
34 | $this->request = $request;
35 | }
36 |
37 | /**
38 | * @param string $URI
39 | * @param string $queryString
40 | *
41 | * @return null|array
42 | */
43 | public function matchURI(string $URI, string $queryString):?array
44 | {
45 | $foundRoute = null;
46 | preg_match($URI, $queryString, $matches);
47 |
48 | if(false === empty($matches)) {
49 | return $matches;
50 | }
51 |
52 | return null;
53 | }
54 |
55 | /**
56 | * @return Route|null
57 | */
58 | public function locate():?Route
59 | {
60 | $foundRoute = null;
61 | $queryString = $this->request->getPath();
62 |
63 | foreach($this->routes as $route) {
64 |
65 | $URI = URIBuilder::build($route->getPattern(), $route->getParameters());
66 |
67 | $matches = $this->matchURI($URI, $queryString);
68 | if(false === empty($matches)) {
69 | $this->handleRequestParameters($route, $matches);
70 | $foundRoute = $route;
71 | break;
72 | }
73 | }
74 |
75 | return $foundRoute;
76 | }
77 |
78 | /**
79 | * @param Route $route
80 | * @param array $parameters
81 | *
82 | * @return Request
83 | */
84 | public function handleRequestParameters(Route $route, array $parameters = []): Request
85 | {
86 | if(isset($parameters[0])) {
87 | unset($parameters[0]);
88 | }
89 |
90 | $parameters = array_values($parameters);
91 |
92 | // Parameters: [0] => 123
93 | // $route->getParameters() : ['id'] => ([0-9]*)
94 | // $route->getParameters() Keys : [0] => 'id'
95 | // Updated Parameters ['id'] => 123
96 | // [] = []
97 |
98 |
99 | $updated = array_combine(array_keys($route->getParameters()), $parameters);
100 |
101 | if($updated) {
102 | $this->request->setParameters($updated);
103 | }
104 |
105 | return $this->request;
106 | }
107 | }
--------------------------------------------------------------------------------
/tests/integration/DBInvoiceEntityTest.php:
--------------------------------------------------------------------------------
1 | setVAT(3)
26 | ->setTotal(3)
27 | ->setReference('FooBar');
28 | $manager = $this->getManager();
29 | $savedEntity = $manager->save($entity);
30 |
31 | $foundEntity = $manager->findOne($savedEntity->getId());
32 |
33 | $this->assertSame($foundEntity->getVat(), $savedEntity->getVat());
34 | $this->assertSame($foundEntity->getTotal(), $savedEntity->getTotal());
35 | $this->assertSame($foundEntity->getReference(), $savedEntity->getReference());
36 | }
37 |
38 | /**
39 | * @return InvoiceManager
40 | */
41 | protected function getManager(): InvoiceManager
42 | {
43 | $connection = new Connection();
44 | $builder = new Builder();
45 | $queryBuilder = new QueryBuilder($builder);
46 | $repository = new InvoiceRepository($connection, $queryBuilder);
47 | return new InvoiceManager($repository);
48 | }
49 |
50 | /**
51 | * @group db-invoice
52 | * @group db-invoice-entity-find-one-by-id-not-exists
53 | */
54 | public function testFindCustomerThatDoesNotExist()
55 | {
56 | $entity = new Invoice();
57 | $entity->setId(5001);
58 | $manager = $this->getManager();
59 |
60 | $foundEntity = $manager->findOne($entity->getId());
61 | $this->assertNull($foundEntity);
62 |
63 | }
64 |
65 | /**
66 | * @group db-invoice
67 | * @group db-invoice-entity-find-all
68 | */
69 | public function testFindAll()
70 | {
71 | $entity1 = new Invoice();
72 | $entity1->setVAT(3)
73 | ->setTotal(3)
74 | ->setReference('Test 1');
75 |
76 | $entity2 = new Invoice();
77 | $entity2->setVAT(4)
78 | ->setTotal(4)
79 | ->setReference('Test 2');
80 |
81 | $manager = $this->getManager();
82 |
83 | $manager->save($entity1);
84 | $manager->save($entity2);
85 | $results = $manager->findAll();
86 |
87 | $this->assertIsArray($results);
88 | $this->assertGreaterThan(1, count($results));
89 |
90 | $foundEntity1 = $results[0];
91 |
92 | $this->assertInstanceOf(Invoice::class, $foundEntity1);
93 | }
94 |
95 | protected function _before()
96 | {
97 | }
98 |
99 | protected function _after()
100 | {
101 | }
102 | }
--------------------------------------------------------------------------------
/src/Helper/HTTP/Request/Request.php:
--------------------------------------------------------------------------------
1 | setPath($queryString);
30 | }
31 |
32 | if( false === empty($method)) {
33 | $this->setMethod($method);
34 | }
35 | }
36 |
37 | /**
38 | * @return string
39 | */
40 | public function getPath(): string
41 | {
42 | return $this->path;
43 | }
44 |
45 | /**
46 | * @param string $queryString
47 | * @return Request
48 | */
49 | public function setPath(string $queryString): Request
50 | {
51 | $path = parse_url($queryString, PHP_URL_PATH);
52 | if(false === empty($path)) {
53 | $this->path = $path;
54 | }
55 |
56 | return $this;
57 | }
58 |
59 | /**
60 | * @return string
61 | */
62 | public function getMethod(): string
63 | {
64 | return $this->method;
65 | }
66 |
67 | /**
68 | * @param string $method
69 | * @return Request
70 | */
71 | public function setMethod(string $method): Request
72 | {
73 | $this->method = strtoupper($method);
74 | return $this;
75 | }
76 |
77 | /**
78 | * @return array
79 | */
80 | public function getParameters(): array
81 | {
82 | return $this->parameters;
83 | }
84 |
85 | /**
86 | * @param array $parameters
87 | * @return Request
88 | */
89 | public function setParameters(array $parameters): Request
90 | {
91 | $this->parameters = $parameters;
92 | foreach($parameters as $key => $value) {
93 | $this->addParameter($key, $value);
94 | }
95 | return $this;
96 | }
97 |
98 | /**
99 | * @param string $key
100 | * @param null|mixed $value
101 | * @return Request
102 | */
103 | public function addParameter(string $key, $value = null) :Request
104 | {
105 | $this->parameters[$key] = $value;
106 |
107 | return $this;
108 | }
109 |
110 | /**
111 | * @param string $key
112 | * @param null|mixed $default
113 | *
114 | * @return mixed
115 | */
116 | public function getParameter(string $key, $default = null)
117 | {
118 | $value = $default;
119 | if(isset($this->parameters[$key])) {
120 | $value = $this->parameters[$key];
121 | }
122 |
123 | return $value;
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/src/Repository/StatusRepository.php:
--------------------------------------------------------------------------------
1 | connection->open();
20 |
21 | $statement = $dbCon->prepare($sql);
22 | $statement->execute([
23 | 'id' => $id
24 | ]);
25 | $row = $statement->fetch();
26 |
27 | if ($row) {
28 | $entity = StatusHydrator::hydrate($row);
29 | }
30 | return $entity;
31 | }
32 |
33 | /**
34 | * @param Status $entity
35 | * @return Status
36 | */
37 | public function saveBKP(Status $entity)
38 | {
39 | $data = [
40 | 'name' => $entity->getName(),
41 | 'internal_name' => $entity->getInternalName(),
42 | ];
43 | $table = 'status';
44 | $where = [];
45 | if (null !== $entity->getId()) {
46 | $where['id'] = $entity->getId();
47 | }
48 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
49 |
50 | if (null !== $entity->getId()) {
51 | $data['id'] = $entity->getId();
52 | }
53 |
54 | $dbCon = $this->connection->open();
55 |
56 | $statement = $dbCon->prepare($sql);
57 | $statement->execute(array_values($data));
58 |
59 | if (null === $entity->getId()) {
60 | $entity->setId((int)$dbCon->lastInsertId());
61 | }
62 |
63 | return $entity;
64 | }
65 |
66 |
67 | /**
68 | * @param Status $entity
69 | * @return Status
70 | */
71 | public function save(Status $entity)
72 | {
73 | $values = [
74 | 'name' => $entity->getName(),
75 | 'internal_name' => $entity->getInternalName(),
76 |
77 | ];
78 | if (null !== $entity->getId()) {
79 | $values['id'] = $entity->getId();
80 | }
81 |
82 | $data = [
83 | 'name' => 'name',
84 | 'internal_name' => 'internal_name'
85 | ];
86 | $table = 'status';
87 | $where = [];
88 | if (null !== $entity->getId()) {
89 | $where['id'] = 'id';
90 | }
91 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
92 |
93 | if (null !== $entity->getId()) {
94 | $data['id'] = $entity->getId();
95 | }
96 |
97 | $dbCon = $this->connection->open();
98 |
99 | $statement = $dbCon->prepare($sql);
100 | $statement->execute($values);
101 |
102 | if (null === $entity->getId()) {
103 | $entity->setId((int)$dbCon->lastInsertId());
104 | }
105 |
106 | return $entity;
107 | }
108 |
109 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Locator/LocatorTest.php:
--------------------------------------------------------------------------------
1 | assertNull($locator->locate());
34 |
35 | }
36 |
37 | /**
38 | * @group locator
39 | * @group locator-valid-route
40 | */
41 | public function testInvoiceID()
42 | {
43 | $route = new Route();
44 | $route->setPattern('/invoice/{id}')
45 | ->setMethods(['GET'])
46 | ->setParameters([
47 | 'id' => '([0-9]*)'
48 | ])
49 | ;
50 | $request = new Request('/invoice/123', 'GET');
51 | $locator = new Locator($request, [$route]);
52 |
53 | $this->assertInstanceOf(Route::class, $locator->locate());
54 |
55 | }
56 |
57 | /**
58 | * @group locator
59 | * @group locator-valid-route
60 | */
61 | public function testInvoiceEdit()
62 | {
63 | $route = new Route();
64 | $route->setPattern('/invoice/{id}/edit')
65 | ->setMethods(['GET'])
66 | ->setParameters([
67 | 'id' => '([0-9]*)'
68 | ])
69 | ;
70 | $request = new Request('/invoice/123/edit', 'GET');
71 | $locator = new Locator($request, [$route]);
72 |
73 | $this->assertInstanceOf(Route::class, $locator->locate());
74 |
75 | }
76 |
77 | /**
78 | * @group locator
79 | * @group locator-invoice-item-edit
80 | */
81 | public function testInvoiceItemEdit()
82 | {
83 | $route1 = new Route();
84 | $route1->setPattern('/invoice/{invoice_id}/item/{item_id}/edit')
85 | ->setMethods(['GET'])
86 | ->setParameters([
87 | 'invoice_id' => '([0-9]*)',
88 | 'item_id' => '([0-9]*)',
89 | ])
90 | ;
91 | $route2 = new Route();
92 | $route2->setPattern('/invoice/{invoice_id}/item/{item_id}/not-valid')
93 | ->setMethods(['GET'])
94 | ->setParameters([
95 | 'invoice_id' => '([0-9]*)',
96 | 'item_id' => '([0-9]*)',
97 | ])
98 | ;
99 | $request = new Request('/invoice/123/item/456/edit', 'GET');
100 | $locator = new Locator($request, [$route1, $route2]);
101 |
102 | $foundRoute = $locator->locate();
103 |
104 | $this->assertInstanceOf(Route::class, $foundRoute);
105 | $this->assertSame('/invoice/{invoice_id}/item/{item_id}/edit', $foundRoute->getPattern());
106 |
107 | }
108 |
109 |
110 | }
--------------------------------------------------------------------------------
/src/Entity/InvoiceItem.php:
--------------------------------------------------------------------------------
1 | reference;
43 | }
44 |
45 |
46 | /**
47 | * @param string $reference
48 | *
49 | * @return InvoiceItem
50 | */
51 | public function setReference(string $reference): InvoiceItem
52 | {
53 | $this->reference = $reference;
54 | return $this;
55 | }
56 |
57 | /**
58 | * @return string
59 | */
60 | public function getDescription(): string
61 | {
62 | return $this->description;
63 | }
64 |
65 | /**
66 | * @return float
67 | */
68 | public function getUnitPrice(): float
69 | {
70 | return $this->unitPrice;
71 | }
72 |
73 | /**
74 | * @return int
75 | */
76 | public function getUnits(): int
77 | {
78 | return $this->units;
79 | }
80 |
81 | /**
82 | * @return float
83 | */
84 | public function getTotal(): float
85 | {
86 | return $this->total;
87 | }
88 |
89 | /**
90 | * @param string $description
91 | * @return InvoiceItem
92 | */
93 | public function setDescription(string $description): InvoiceItem
94 | {
95 | $this->description = $description;
96 | return $this;
97 | }
98 |
99 | /**
100 | * @param int $units
101 | * @return InvoiceItem
102 | */
103 | public function setUnits(int $units): InvoiceItem
104 | {
105 | $this->units = $units;
106 | return $this;
107 | }
108 |
109 | /**
110 | * @param float $unitPrice
111 | * @return InvoiceItem
112 | */
113 | public function setUnitPrice(float $unitPrice): InvoiceItem
114 | {
115 | $this->unitPrice = $unitPrice;
116 | return $this;
117 | }
118 |
119 | /**
120 | * @param float $total
121 | * @return InvoiceItem
122 | */
123 | public function setTotal(float $total): InvoiceItem
124 | {
125 | $this->total = $total;
126 | return $this;
127 | }
128 |
129 | /**
130 | * @return Invoice
131 | */
132 | public function getInvoice(): Invoice
133 | {
134 | return $this->invoice;
135 | }
136 |
137 | /**
138 | * @param Invoice $invoice
139 | * @return InvoiceItem
140 | */
141 | public function setInvoice(Invoice $invoice): InvoiceItem
142 | {
143 | $this->invoice = $invoice;
144 | return $this;
145 | }
146 |
147 |
148 | }
--------------------------------------------------------------------------------
/tests/integration/DBCustomerEntityTest.php:
--------------------------------------------------------------------------------
1 | setFirstName('Foo')
28 | ->setLastName('Bar')
29 | ->setCompanyName('FooBar');
30 | $manager = $this->getManager();
31 | $savedEntity = $manager->save($entity);
32 |
33 | $foundEntity = $manager->findOne($savedEntity->getId());
34 |
35 | $this->assertSame($foundEntity->getFirstName(), $savedEntity->getFirstName());
36 | $this->assertSame($foundEntity->getLastName(), $savedEntity->getLastName());
37 | $this->assertSame($foundEntity->getCompanyName(), $savedEntity->getCompanyName());
38 | }
39 |
40 | /**
41 | * @return CustomerManager
42 | */
43 | protected function getManager(): CustomerManager
44 | {
45 | $connection = new Connection();
46 | $builder = new Builder();
47 | $queryBuilder = new QueryBuilder($builder);
48 | $repository = new CustomerRepository($connection, $queryBuilder);
49 | return new CustomerManager($repository);
50 | }
51 |
52 | /**
53 | * @group db-customer
54 | * @group db-customer-entity-find-one-by-id-not-exists
55 | */
56 | public function testFindCustomerThatDoesNotExist()
57 | {
58 | $entity = new Customer();
59 | $entity->setId(5001);
60 | $manager = $this->getManager();
61 |
62 | $foundEntity = $manager->findOne($entity->getId());
63 | $this->assertNull($foundEntity);
64 |
65 | }
66 |
67 | /**
68 | * @group db-customer
69 | * @group db-customer-entity-find-all
70 | */
71 | public function testFindAll()
72 | {
73 | $entity1 = new Customer();
74 | $entity1->setFirstName("Peter");
75 | $entity1->setLastName("Fisher");
76 | $entity1->setCompanyName("How To Code Well");
77 |
78 | $entity2 = new Customer();
79 | $entity2->setFirstName("Foo");
80 | $entity2->setLastName("Bar");
81 | $entity2->setCompanyName("FooBar");
82 |
83 | $manager = $this->getManager();
84 |
85 | $manager->save($entity1);
86 | $manager->save($entity2);
87 | $results = $manager->findAll();
88 |
89 | $this->assertIsArray($results);
90 | $this->assertGreaterThan(1, count($results));
91 |
92 | $foundEntity1 = $results[0];
93 |
94 | $this->assertInstanceOf(Customer::class, $foundEntity1);
95 | }
96 |
97 | protected function _before()
98 | {
99 | }
100 |
101 | protected function _after()
102 | {
103 | }
104 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Route/FactoryTest.php:
--------------------------------------------------------------------------------
1 | addRoute([
31 | 'pattern' => '/',
32 | 'controller' => Type\Home::class,
33 | 'method' => ['GET'],
34 | 'action' => 'index'
35 | ]);
36 |
37 | $this->assertInstanceOf(Route::class, $route);
38 | }
39 |
40 | /**
41 | * @group router
42 | * @group router-factory-make-multiple
43 | */
44 | public function testMakeMultiple()
45 | {
46 | $routes = [
47 | [
48 | 'pattern' => '/',
49 | 'controller' => Type\Home::class,
50 | 'method' => ['GET'],
51 | 'action' => 'index'
52 | ],
53 | [
54 | 'pattern' => '/invoice/([0-9]*)',
55 | 'controller' => Type\Invoice::class,
56 | 'method' => ['GET'],
57 | 'action' => 'index'
58 | ],
59 | [
60 | 'pattern' => '/invoice/([0-9]*)/edit/([0-9]*)',
61 | 'controller' => Type\Invoice::class,
62 | 'method' => ['GET'],
63 | 'action' => 'index'
64 | ],
65 | ];
66 |
67 | $factory = new Factory();
68 |
69 | $results = [];
70 | foreach($routes as $data) {
71 | $results[] = $factory->addRoute($data);
72 | }
73 |
74 | $this->assertEquals(count($routes), count($results));
75 | }
76 |
77 | /**
78 | * @group router
79 | * @group router-factory-make-routes
80 | */
81 | public function testMakeRoutes()
82 | {
83 | $routes = [
84 | [
85 | 'pattern' => '/',
86 | 'controller' => Type\Home::class,
87 | 'method' => ['GET'],
88 | 'action' => 'index'
89 | ],
90 | [
91 | 'pattern' => '/invoice/([0-9]*)',
92 | 'controller' => Type\Invoice::class,
93 | 'method' => ['GET'],
94 | 'action' => 'index'
95 | ],
96 | [
97 | 'pattern' => '/invoice/([0-9]*)/edit/([0-9]*)',
98 | 'controller' => Type\Invoice::class,
99 | 'method' => ['GET'],
100 | 'action' => 'index'
101 | ],
102 | ];
103 |
104 | $factory = new Factory();
105 |
106 | $results = $factory->makeRoutes($routes);
107 | $this->assertIsArray($results);
108 | $this->assertEquals(count($routes), count($results));
109 | }
110 | }
--------------------------------------------------------------------------------
/tests/unit/Entity/AddInvoiceItemToInvoiceTest.php:
--------------------------------------------------------------------------------
1 | addItem($invoiceItem);
31 | $this->assertInstanceOf(Invoice::class, $invoice);
32 | }
33 |
34 | /**
35 | * @group entity
36 | * @group invoice-add-item
37 | * @group invoice-add-item-set-items
38 | */
39 | public function testSetItems()
40 | {
41 | $invoiceItem1 = new InvoiceItem();
42 | $invoiceItem2 = new InvoiceItem();
43 | $invoice = new Invoice();
44 | $invoice->setItems([$invoiceItem1]);
45 | $invoice->setItems([$invoiceItem2]);
46 | $this->assertContains($invoiceItem2, $invoice->getItems());
47 | $this->assertCount(1, $invoice->getItems());
48 | $this->assertInstanceOf(Invoice::class, $invoice);
49 | }
50 |
51 | /**
52 | * @group entity
53 | * @group invoice-add-item
54 | * @group invoice-add-item-reset-items
55 | */
56 | public function testResetItems()
57 | {
58 | $invoiceItem = new InvoiceItem();
59 | $invoice = new Invoice();
60 | $invoice->setItems([$invoiceItem]);
61 | $invoice->resetItems();
62 | $this->assertEmpty($invoice->getItems());
63 | $this->assertInstanceOf(Invoice::class, $invoice);
64 | }
65 |
66 | /**
67 | * @group entity
68 | * @group invoice-add-item
69 | * @group invoice-add-item-does-contain-item
70 | */
71 | public function testDoesContainItem()
72 | {
73 | $invoiceItem = new InvoiceItem();
74 | $invoice = new Invoice();
75 | $invoice->addItem($invoiceItem);
76 | $doesItemsContain = $invoice->doesItemsContain($invoiceItem);
77 |
78 | $this->assertTrue($doesItemsContain);
79 | $this->assertInstanceOf(Invoice::class, $invoice);
80 | }
81 |
82 | /**
83 | * @group entity
84 | * @group invoice-add-item
85 | * @group invoice-add-item-does-not-contain-item
86 | */
87 | public function testDoesNotContainItem()
88 | {
89 | $invoiceItem = new InvoiceItem();
90 | $invoice = new Invoice();
91 | $doesItemsContain = $invoice->doesItemsContain($invoiceItem);
92 |
93 | $this->assertFalse($doesItemsContain);
94 | $this->assertInstanceOf(Invoice::class, $invoice);
95 | }
96 |
97 | /**
98 | * @group entity
99 | * @group invoice-add-item
100 | * @group invoice-add-item-does-not-contain-item
101 | */
102 | public function testSetTheSameItem()
103 | {
104 | $invoiceItem = new InvoiceItem();
105 | $invoice = new Invoice();
106 | $invoice->setItems([$invoiceItem, $invoiceItem]);
107 | $this->assertCount(1, $invoice->getItems());
108 | $this->assertInstanceOf(Invoice::class, $invoice);
109 | }
110 | }
--------------------------------------------------------------------------------
/tests/unit/Entity/InvoiceItemTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty($invoiceItem->getDescription());
30 | $this->assertIsString($invoiceItem->getDescription());
31 | $this->assertIsFloat($invoiceItem->getUnitPrice());
32 | $this->assertSame(0.0,$invoiceItem->getUnitPrice());
33 | $this->assertIsInt($invoiceItem->getUnits());
34 | $this->assertSame(0,$invoiceItem->getUnits());
35 | $this->assertEmpty($invoiceItem->getReference());
36 | $this->assertIsString($invoiceItem->getReference());
37 | $this->assertIsFloat($invoiceItem->getTotal());
38 | $this->assertSame(0.0,$invoiceItem->getTotal());
39 |
40 | }
41 |
42 | /**
43 | * @group entity
44 | * @group invoice-item
45 | * @group invoice-item-set-description
46 | */
47 | public function testDescription()
48 | {
49 | $invoiceItem = new InvoiceItem();
50 | $item = $invoiceItem->setDescription('Test invoice item');
51 |
52 | $this->assertInstanceOf(InvoiceItem::class, $item);
53 | $this->assertSame('Test invoice item', $invoiceItem->getDescription());
54 | }
55 |
56 | /**
57 | * @group entity
58 | * @group invoice-item
59 | * @group invoice-item-set-reference
60 | */
61 | public function testReference()
62 | {
63 | $invoiceItem = new InvoiceItem();
64 | $item = $invoiceItem->setReference('Test invoice item reference');
65 |
66 | $this->assertInstanceOf(InvoiceItem::class, $item);
67 | $this->assertSame('Test invoice item reference', $invoiceItem->getReference());
68 | }
69 |
70 |
71 | /**
72 | * @group entity
73 | * @group invoice-item
74 | * @group invoice-item-set-units
75 | */
76 | public function testUnits()
77 | {
78 | $invoiceItem = new InvoiceItem();
79 | $item = $invoiceItem->setUnits(4);
80 |
81 | $this->assertInstanceOf(InvoiceItem::class, $item);
82 | $this->assertSame(4, $invoiceItem->getUnits());
83 | }
84 |
85 | /**
86 | * @group entity
87 | * @group invoice-item
88 | * @group invoice-item-set-unit-price
89 | */
90 | public function testUnitPrice()
91 | {
92 | $invoiceItem = new InvoiceItem();
93 | $item = $invoiceItem->setUnitPrice(223423.53);
94 |
95 | $this->assertInstanceOf(InvoiceItem::class, $item);
96 | $this->assertSame(223423.53, $invoiceItem->getUnitPrice());
97 | }
98 |
99 | /**
100 | * @group entity
101 | * @group invoice-item
102 | * @group invoice-item-set-total
103 | */
104 | public function testTotal()
105 | {
106 | $invoiceItem = new InvoiceItem();
107 | $item = $invoiceItem->setTotal(124434235.53);
108 |
109 | $this->assertInstanceOf(InvoiceItem::class, $item);
110 | $this->assertSame(124434235.53, $invoiceItem->getTotal());
111 | }
112 | }
--------------------------------------------------------------------------------
/tests/unit/Helper/HTTP/Request/RequestTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty($route->getPath());
30 | $this->assertEmpty($route->getMethod());
31 | $this->assertEmpty($route->getParameters());
32 | $this->assertIsArray($route->getParameters());
33 | }
34 |
35 | /**
36 | * @group request
37 | * @group request-set-URI-in-constructor
38 | */
39 | public function testSetURIInConstructor()
40 | {
41 | $route = new Request('/');
42 |
43 | $this->assertSame('/', $route->getPath());
44 |
45 | }
46 |
47 | /**
48 | * @group request
49 | * @group request-set-URI
50 | */
51 | public function testSetURI()
52 | {
53 | $route = new Request();
54 |
55 | $route->setPath('/');
56 |
57 | $this->assertSame('/', $route->getPath());
58 |
59 | }
60 |
61 | /**
62 | * @group request
63 | * @group request-set-method-in-constructor
64 | */
65 | public function testSetMethodInConstructor()
66 | {
67 | $route = new Request('/', 'GET');
68 |
69 | $this->assertSame('GET', $route->getMethod());
70 |
71 | }
72 |
73 | /**
74 | * @group request
75 | * @group request-set-method
76 | */
77 | public function testSetMethod()
78 | {
79 | $route = new Request();
80 |
81 | $route->setMethod('GET');
82 |
83 | $this->assertSame('GET', $route->getMethod());
84 |
85 | }
86 |
87 | /**
88 | * @group request
89 | * @group request-set-method
90 | */
91 | public function testSetLowerCaseMethod()
92 | {
93 | $route = new Request();
94 |
95 | $route->setMethod('get');
96 |
97 | $this->assertSame('GET', $route->getMethod());
98 |
99 | }
100 |
101 | /**
102 | * @group request
103 | * @group request-add-parameter
104 | */
105 | public function testAddParameter()
106 | {
107 | $route = new Request();
108 |
109 | $route->addParameter('id', 123);
110 |
111 | $this->assertSame(123, $route->getParameter('id'));
112 |
113 | }
114 |
115 | /**
116 | * @group request
117 | * @group request-add-parameter
118 | */
119 | public function testSetParameters()
120 | {
121 | $route = new Request();
122 |
123 | $route->setParameters([
124 | 'id' => 123
125 | ]);
126 |
127 | $this->assertArrayHasKey('id', $route->getParameters());
128 |
129 | }
130 |
131 | /**
132 | * @group request
133 | * @group request-add-parameter
134 | */
135 | public function testGetDefaultParameter()
136 | {
137 | $route = new Request();
138 |
139 | $this->assertSame(345, $route->getParameter('id', 345));
140 |
141 | }
142 |
143 | /**
144 | * @group request
145 | * @group request-add-parameter
146 | */
147 | public function testGetNullParameter()
148 | {
149 | $route = new Request();
150 |
151 | $this->assertNull($route->getParameter('id'));
152 |
153 | }
154 | }
--------------------------------------------------------------------------------
/src/Entity/Invoice.php:
--------------------------------------------------------------------------------
1 | reference;
42 | }
43 |
44 | /**
45 | * @return null
46 | */
47 | public function getCustomer()
48 | {
49 | return $this->customer;
50 | }
51 |
52 | /**
53 | * @return array
54 | */
55 | public function getItems(): array
56 | {
57 | return $this->items;
58 | }
59 |
60 | /**
61 | * @return null|Status
62 | */
63 | public function getStatus():?Status
64 | {
65 | return $this->status;
66 | }
67 |
68 | /**
69 | * @return float
70 | */
71 | public function getTotal(): float
72 | {
73 | return $this->total;
74 | }
75 |
76 | /**
77 | * @return float
78 | */
79 | public function getVat(): float
80 | {
81 | return $this->vat;
82 | }
83 |
84 | /**
85 | * @param string $reference
86 | *
87 | * @return Invoice
88 | */
89 | public function setReference(string $reference): Invoice
90 | {
91 | $this->reference = $reference;
92 | return $this;
93 | }
94 |
95 | /**
96 | * @param float $total
97 | *
98 | * @return Invoice
99 | */
100 | public function setTotal(float $total): Invoice
101 | {
102 | $this->total = $total;
103 | return $this;
104 | }
105 |
106 | /**
107 | * @param float $vat
108 | *
109 | * @return Invoice
110 | */
111 | public function setVAT(float $vat): Invoice
112 | {
113 | $this->vat = $vat;
114 | return $this;
115 | }
116 |
117 | /**
118 | * @param InvoiceItem $invoiceItem
119 | *
120 | * @return Invoice
121 | */
122 | public function addItem(InvoiceItem $invoiceItem): Invoice
123 | {
124 | if(false === $this->doesItemsContain($invoiceItem)) {
125 | $this->items[] = $invoiceItem;
126 | }
127 |
128 | return $this;
129 | }
130 |
131 | /**
132 | * @param array $items
133 | * @return Invoice
134 | */
135 | public function setItems(array $items): Invoice
136 | {
137 | $this->resetItems();
138 | foreach($items as $item) {
139 | $this->addItem($item);
140 | }
141 | return $this;
142 | }
143 |
144 | /**
145 | * @return Invoice
146 | */
147 | public function resetItems(): Invoice
148 | {
149 | $this->items = [];
150 | return $this;
151 | }
152 |
153 | /**
154 | * @param InvoiceItem $object
155 | * @return bool
156 | */
157 | public function doesItemsContain(InvoiceItem $object):bool
158 | {
159 | foreach ($this->getItems() as $item) {
160 | if($item === $object) {
161 | return true;
162 | }
163 | }
164 | return false;
165 | }
166 |
167 | /**
168 | * @param null $status
169 | * @return Invoice
170 | */
171 | public function setStatus($status):Invoice
172 | {
173 | $this->status = $status;
174 | return $this;
175 | }
176 |
177 | /**
178 | * @param null $customer
179 | * @return Invoice
180 | */
181 | public function setCustomer($customer):Invoice
182 | {
183 | $this->customer = $customer;
184 | return $this;
185 | }
186 |
187 | }
--------------------------------------------------------------------------------
/tests/acceptance/DBInvoiceEntityCest.php:
--------------------------------------------------------------------------------
1 | setVAT(1)
28 | ->setTotal(1)
29 | ->setReference('Test reference');
30 | $manager = $this->getInvoiceManager();
31 | $manager->save($entity);
32 |
33 | $I->seeInDatabase('invoice', [
34 | 'vat' => $entity->getVat(),
35 | 'total' => $entity->getTotal(),
36 | 'reference' => $entity->getReference()
37 | ]);
38 | }
39 |
40 | /**
41 | * @return InvoiceManager
42 | */
43 | protected function getInvoiceManager(): InvoiceManager
44 | {
45 | $connection = new Connection();
46 | $builder = new Builder();
47 | $queryBuilder = new QueryBuilder($builder);
48 | $repository = new InvoiceRepository($connection, $queryBuilder);
49 | return new InvoiceManager($repository);
50 | }
51 |
52 | /**
53 | * @return StatusManager
54 | */
55 | protected function getStatusManager(): StatusManager
56 | {
57 | $connection = new Connection();
58 | $builder = new Builder();
59 | $queryBuilder = new QueryBuilder($builder);
60 | $repository = new StatusRepository($connection, $queryBuilder);
61 | return new StatusManager($repository);
62 | }
63 |
64 | /**
65 | * @param AcceptanceTester $I
66 | * @group db
67 | * @group db-invoice-entity-update
68 | */
69 | public function updateTest(AcceptanceTester $I)
70 | {
71 | $entity = new Invoice();
72 | $entity->setVAT(1)
73 | ->setTotal(1)
74 | ->setReference('Test reference');
75 | $manager = $this->getInvoiceManager();
76 | $savedEntity = $manager->save($entity);
77 |
78 | $I->seeInDatabase('invoice', [
79 | 'vat' => $entity->getVat(),
80 | 'total' => $entity->getTotal(),
81 | 'reference' => $entity->getReference(),
82 | 'id' => $savedEntity->getId()
83 | ]);
84 |
85 | $savedEntity->setReference('Test updated reference');
86 | $savedEntity->setTotal(3)
87 | ->setVAT(2);
88 | $manager->save($savedEntity);
89 |
90 | $I->seeInDatabase('invoice', [
91 | 'vat' => $savedEntity->getVat(),
92 | 'total' => $savedEntity->getTotal(),
93 | 'reference' => $savedEntity->getReference(),
94 | 'id' => $savedEntity->getId()
95 | ]);
96 |
97 | }
98 |
99 | /**
100 | * @param AcceptanceTester $I
101 | * @group db
102 | * @group db-invoice-entity-insert-with-status
103 | */
104 | public function insertWithStatusTest(AcceptanceTester $I)
105 | {
106 | $status = new Status();
107 | $status->setName('temp')
108 | ->setInternalName('TEMP');
109 |
110 | $savedStatus = $this->getStatusManager()->save($status);
111 |
112 | $invoice = new Invoice();
113 | $invoice->setVAT(1)
114 | ->setTotal(1)
115 | ->setStatus($savedStatus)
116 | ->setReference('Test reference');
117 |
118 | $manager = $this->getInvoiceManager();;
119 | $savedInvoice = $manager->save($invoice);
120 |
121 | $I->seeInDatabase('invoice', [
122 | 'vat' => $invoice->getVat(),
123 | 'total' => $invoice->getTotal(),
124 | 'reference' => $invoice->getReference(),
125 | 'status_id' => $savedStatus->getId(),
126 | 'id' => $savedInvoice->getId()
127 | ]);
128 |
129 |
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/src/DB/QueryBuilder.php:
--------------------------------------------------------------------------------
1 | 0) {
43 | $sql .= ", ";
44 | }
45 | $sql .= ':'.$placeholders[$i];
46 | }
47 |
48 | return $sql;
49 | }
50 |
51 | /**
52 | * @param array $data
53 | * @return string
54 | */
55 | public static function fields(array $data = []):string
56 | {
57 | $placeholders = array_keys($data);
58 | $sql = '';
59 | for ($i = 0; $i < count($placeholders); $i++) {
60 | if ($i > 0) {
61 | $sql .= ", ";
62 | }
63 | $sql .= "`".$placeholders[$i]."`";
64 | }
65 |
66 | return $sql;
67 | }
68 |
69 |
70 | /**
71 | * @param array $data
72 | * @return string
73 | */
74 | public static function updatePlaceholders(array $data = []):string
75 | {
76 | $sql = '';
77 | $counter = 0;
78 | $total = count($data);
79 | foreach($data as $field => $value) {
80 | $counter ++;
81 | $sql.='`'.$field.'` =:'.$field;
82 | if($counter < $total) {
83 | $sql.= ', ';
84 | } else{
85 | $sql.= ' ';
86 | }
87 | }
88 | return $sql;
89 | }
90 |
91 | /**
92 | * @param array $data
93 | * @param string $table
94 | * @param array $where
95 | * @return string
96 | */
97 | public static function insertOrUpdate(array $data, string $table, array $where = []): string
98 | {
99 | if(empty($where)) {
100 | $sql = self::insert($data, $table);
101 | } else {
102 | $sql = self::update($data, $table, $where);
103 | }
104 |
105 | return $sql;
106 | }
107 |
108 | /**
109 | * @param string $table
110 | * @return string
111 | */
112 | public static function findOneBy(string $table)
113 | {
114 | return "SELECT * FROM `".$table."` WHERE id=:id";
115 | }
116 |
117 | /**
118 | * @param string $table
119 | * @return string
120 | */
121 | public static function findAll(string $table)
122 | {
123 | return "SELECT * FROM `".$table."`";
124 | }
125 |
126 | /**
127 | * @param array $conditions
128 | * @return string
129 | */
130 | public static function where(array $conditions = []) {
131 | $sql ='';
132 |
133 | $total = count($conditions);
134 | $num = 0;
135 | foreach($conditions as $field => $value) {
136 | $num ++;
137 | $sql.='`'.$field.'` =:'.$field;
138 | if($num < $total) {
139 | $sql.=' AND ';
140 | }
141 |
142 | }
143 |
144 | return $sql;
145 | }
146 |
147 | /**
148 | * @param string $table
149 | * @param array $where
150 | * @return string
151 | */
152 | public static function findAllBy(string $table, array $where = [])
153 | {
154 | $sql = "SELECT * FROM `".$table."` WHERE " .self::where($where);
155 |
156 | return $sql;
157 | }
158 | }
--------------------------------------------------------------------------------
/tests/_data/dump.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.26, for Linux (x86_64)
2 | --
3 | -- Host: localhost Database: test_invoice_app
4 | -- ------------------------------------------------------
5 | -- Server version 5.7.26
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `customer`
20 | --
21 |
22 | DROP TABLE IF EXISTS `customer`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `customer` (
26 | `id` int(11) NOT NULL AUTO_INCREMENT,
27 | `company_name` varchar(250) NOT NULL,
28 | `first_name` varchar(250) NOT NULL,
29 | `last_name` varchar(250) DEFAULT NULL,
30 | `date_created` datetime DEFAULT CURRENT_TIMESTAMP,
31 | `date_updated` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
32 | PRIMARY KEY (`id`)
33 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
34 | /*!40101 SET character_set_client = @saved_cs_client */;
35 |
36 | --
37 | -- Dumping data for table `customer`
38 | --
39 |
40 | LOCK TABLES `customer` WRITE;
41 | /*!40000 ALTER TABLE `customer` DISABLE KEYS */;
42 | /*!40000 ALTER TABLE `customer` ENABLE KEYS */;
43 | UNLOCK TABLES;
44 |
45 | --
46 | -- Table structure for table `invoice`
47 | --
48 |
49 | DROP TABLE IF EXISTS `invoice`;
50 | /*!40101 SET @saved_cs_client = @@character_set_client */;
51 | /*!40101 SET character_set_client = utf8 */;
52 | CREATE TABLE `invoice` (
53 | `id` int(11) NOT NULL AUTO_INCREMENT,
54 | `reference` varchar(250) NOT NULL,
55 | `total` decimal(10,0) DEFAULT NULL,
56 | `vat` decimal(10,0) DEFAULT NULL,
57 | `status_id` int(11) DEFAULT NULL,
58 | `customer_id` int(11) DEFAULT NULL,
59 | `date_created` datetime DEFAULT CURRENT_TIMESTAMP,
60 | `date_updated` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
61 | PRIMARY KEY (`id`),
62 | KEY `fk_status` (`status_id`),
63 | KEY `fk_customer` (`customer_id`),
64 | CONSTRAINT `invoice_ibfk_1` FOREIGN KEY (`status_id`) REFERENCES `status` (`id`),
65 | CONSTRAINT `invoice_ibfk_2` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`)
66 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
67 | /*!40101 SET character_set_client = @saved_cs_client */;
68 |
69 | --
70 | -- Dumping data for table `invoice`
71 | --
72 |
73 | LOCK TABLES `invoice` WRITE;
74 | /*!40000 ALTER TABLE `invoice` DISABLE KEYS */;
75 | /*!40000 ALTER TABLE `invoice` ENABLE KEYS */;
76 | UNLOCK TABLES;
77 |
78 | --
79 | -- Table structure for table `invoice_item`
80 | --
81 |
82 | DROP TABLE IF EXISTS `invoice_item`;
83 | /*!40101 SET @saved_cs_client = @@character_set_client */;
84 | /*!40101 SET character_set_client = utf8 */;
85 | CREATE TABLE `invoice_item` (
86 | `id` int(11) NOT NULL AUTO_INCREMENT,
87 | `reference` varchar(250) DEFAULT NULL,
88 | `description` varchar(250) NOT NULL,
89 | `unit_price` decimal(10,0) NOT NULL,
90 | `units` int(11) NOT NULL,
91 | `total` decimal(10,0) NOT NULL,
92 | `invoice_id` int(11) NOT NULL,
93 | `date_created` datetime DEFAULT CURRENT_TIMESTAMP,
94 | `date_updated` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
95 | PRIMARY KEY (`id`),
96 | KEY `fk_invoice` (`invoice_id`),
97 | CONSTRAINT `invoice_item_ibfk_1` FOREIGN KEY (`invoice_id`) REFERENCES `invoice` (`id`)
98 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
99 | /*!40101 SET character_set_client = @saved_cs_client */;
100 |
101 | --
102 | -- Dumping data for table `invoice_item`
103 | --
104 |
105 | LOCK TABLES `invoice_item` WRITE;
106 | /*!40000 ALTER TABLE `invoice_item` DISABLE KEYS */;
107 | /*!40000 ALTER TABLE `invoice_item` ENABLE KEYS */;
108 | UNLOCK TABLES;
109 |
110 | --
111 | -- Table structure for table `status`
112 | --
113 |
114 | DROP TABLE IF EXISTS `status`;
115 | /*!40101 SET @saved_cs_client = @@character_set_client */;
116 | /*!40101 SET character_set_client = utf8 */;
117 | CREATE TABLE `status` (
118 | `id` int(11) NOT NULL AUTO_INCREMENT,
119 | `name` varchar(250) NOT NULL,
120 | `internal_name` varchar(250) NOT NULL,
121 | `date_created` datetime DEFAULT CURRENT_TIMESTAMP,
122 | `date_updated` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
123 | PRIMARY KEY (`id`)
124 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
125 | /*!40101 SET character_set_client = @saved_cs_client */;
126 |
127 | --
128 | -- Dumping data for table `status`
129 | --
130 |
131 | LOCK TABLES `status` WRITE;
132 | /*!40000 ALTER TABLE `status` DISABLE KEYS */;
133 | INSERT INTO `status` VALUES (1,'Draft','DRAFT','2019-07-25 07:07:59',NULL),(2,'Sent','SENT','2019-07-25 07:07:59',NULL),(3,'Overdue','OVERDUE','2019-07-25 07:07:59',NULL);
134 | /*!40000 ALTER TABLE `status` ENABLE KEYS */;
135 | UNLOCK TABLES;
136 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
137 |
138 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
139 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
140 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
141 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
142 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
143 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
144 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
145 |
146 | -- Dump completed on 2019-07-25 7:08:08
147 |
--------------------------------------------------------------------------------
/tests/integration/DBInvoiceItemEntityTest.php:
--------------------------------------------------------------------------------
1 | setReference('Test Invoice');
29 |
30 | $invoiceManager = $this->getInvoiceManager();
31 | $savedInvoice = $invoiceManager->save($invoice);
32 |
33 | $foundInvoice = $invoiceManager->findOne($savedInvoice->getId());
34 |
35 | $this->assertSame($foundInvoice->getReference(), $savedInvoice->getReference());
36 |
37 | $entity = new InvoiceItem();
38 | $entity->setReference('Devtime')
39 | ->setTotal(150)
40 | ->setUnits(7)
41 | ->setUnitPrice(100)
42 | ->setDescription('Development time')
43 | ->setInvoice($invoice);
44 |
45 | $invoiceItemManager = $this->getManager();
46 | $savedEntity = $invoiceItemManager->save($entity);
47 |
48 | $this->assertInstanceOf(InvoiceItem::class, $savedEntity);
49 |
50 | $foundEntity = $invoiceItemManager->findOne($savedEntity->getId());
51 |
52 | $this->assertInstanceOf(InvoiceItem::class, $foundEntity);
53 |
54 | $this->assertSame($foundEntity->getReference(), $savedEntity->getReference());
55 | $this->assertSame($foundEntity->getTotal(), $savedEntity->getTotal());
56 | $this->assertSame($foundEntity->getUnits(), $savedEntity->getUnits());
57 | $this->assertSame($foundEntity->getUnitPrice(), $savedEntity->getUnitPrice());
58 | $this->assertSame($foundEntity->getDescription(), $savedEntity->getDescription());
59 | }
60 |
61 | /**
62 | * @return InvoiceManager
63 | */
64 | protected function getInvoiceManager(): InvoiceManager
65 | {
66 | $connection = new Connection();
67 | $builder = new Builder();
68 | $queryBuilder = new QueryBuilder($builder);
69 | $repository = new InvoiceRepository($connection, $queryBuilder);
70 | return new InvoiceManager($repository);
71 | }
72 |
73 | /**
74 | * @return InvoiceItemManager
75 | */
76 | protected function getManager(): InvoiceItemManager
77 | {
78 | $connection = new Connection();
79 | $builder = new Builder();
80 | $queryBuilder = new QueryBuilder($builder);
81 | $repository = new InvoiceItemRepository($connection, $queryBuilder);
82 | return new InvoiceItemManager($repository);
83 | }
84 |
85 | /**
86 | * @group db-invoice-item
87 | * @group db-invoice-item-entity-find-one-by-id-not-exists
88 | */
89 | public function testFindInvoiceItemThatDoesNotExist()
90 | {
91 | $entity = new InvoiceItem();
92 | $entity->setId(5001);
93 | $manager = $this->getManager();
94 |
95 | $foundEntity = $manager->findOne($entity->getId());
96 | $this->assertNull($foundEntity);
97 |
98 | }
99 |
100 | /**
101 | * @group db-invoice-item
102 | * @group db-invoice-item-entity-find-all
103 | */
104 | public function testFindAllInvoiceItems()
105 | {
106 | $invoice = new Invoice();
107 | $invoice->setReference('Test Invoice');
108 |
109 | $invoiceManager = $this->getInvoiceManager();
110 | $savedInvoice = $invoiceManager->save($invoice);
111 |
112 | $foundInvoice = $invoiceManager->findOne($savedInvoice->getId());
113 |
114 | $this->assertSame($foundInvoice->getReference(), $savedInvoice->getReference());
115 |
116 | $entity1 = new InvoiceItem();
117 | $entity1->setReference('Devtime')
118 | ->setTotal(150)
119 | ->setUnits(7)
120 | ->setUnitPrice(100)
121 | ->setDescription('Development time')
122 | ->setInvoice($invoice);
123 |
124 | $entity2 = new InvoiceItem();
125 | $entity2->setReference('Devtime')
126 | ->setTotal(150)
127 | ->setUnits(7)
128 | ->setUnitPrice(100)
129 | ->setDescription('Development time')
130 | ->setInvoice($invoice);
131 |
132 | $manager = $this->getManager();
133 |
134 | $manager->save($entity1);
135 | $manager->save($entity2);
136 |
137 | $results = $manager->findAll();
138 | $this->assertIsArray($results);
139 | $this->assertGreaterThan(1, count($results));
140 |
141 | $foundEntity1 = $results[0];
142 | $this->assertInstanceOf(InvoiceItem::class, $foundEntity1);
143 |
144 | }
145 |
146 |
147 | /**
148 | * @group db-invoice-item
149 | * @group db-invoice-item-entity-find-all-by-invoice-id
150 | */
151 | public function testFindAllInvoiceItemsByInvoiceID()
152 | {
153 | $invoice = new Invoice();
154 | $invoice->setReference('Test Invoice');
155 |
156 | $invoiceManager = $this->getInvoiceManager();
157 | $savedInvoice = $invoiceManager->save($invoice);
158 |
159 | $foundInvoice = $invoiceManager->findOne($savedInvoice->getId());
160 |
161 | $this->assertSame($foundInvoice->getReference(), $savedInvoice->getReference());
162 |
163 | $entity1 = new InvoiceItem();
164 | $entity1->setReference('Devtime')
165 | ->setTotal(150)
166 | ->setUnits(7)
167 | ->setUnitPrice(100)
168 | ->setDescription('Development time')
169 | ->setInvoice($invoice);
170 |
171 | $entity2 = new InvoiceItem();
172 | $entity2->setReference('Devtime')
173 | ->setTotal(150)
174 | ->setUnits(7)
175 | ->setUnitPrice(100)
176 | ->setDescription('Development time')
177 | ->setInvoice($invoice);
178 |
179 | $manager = $this->getManager();
180 |
181 | $manager->save($entity1);
182 | $manager->save($entity2);
183 |
184 |
185 | $results = $manager->findAllByInvoiceID($foundInvoice->getId());
186 | $this->assertIsArray($results);
187 | $this->assertGreaterThan(1, count($results));
188 |
189 | $foundEntity1 = $results[0];
190 | $this->assertInstanceOf(InvoiceItem::class, $foundEntity1);
191 |
192 | }
193 |
194 | protected function _before()
195 | {
196 | }
197 |
198 | protected function _after()
199 | {
200 | }
201 | }
--------------------------------------------------------------------------------
/tests/unit/DB/QueryBuilderTest.php:
--------------------------------------------------------------------------------
1 | builder = $builder;
25 | }
26 |
27 | /**
28 | * @group entity
29 | * @group db
30 | * @group db-query-builder
31 | * @group db-query-builder-select
32 | */
33 | public function testSelect()
34 | {
35 | $queryBuilder = new QueryBuilder($this->builder);
36 | $queryBuilder->select('invoice');
37 |
38 | $this->assertSame('SELECT * FROM `invoice`', $queryBuilder->getSQL());
39 | }
40 |
41 | /**
42 | * @group entity
43 | * @group db
44 | * @group db-query-builder
45 | * @group db-query-builder-select-with-fields
46 | */
47 | public function testSelectWithFields()
48 | {
49 | $queryBuilder = new QueryBuilder($this->builder);
50 | $queryBuilder->select('invoice', ['id', 'total']);
51 |
52 | $this->assertSame('SELECT `id`, `total` FROM `invoice`', $queryBuilder->getSQL());
53 | }
54 |
55 |
56 | /**
57 | * @group entity
58 | * @group db
59 | * @group db-query-builder
60 | * @group db-query-builder-and-where
61 | */
62 | public function testAndWhere()
63 | {
64 | $queryBuilder = new QueryBuilder($this->builder);
65 | $queryBuilder->andWhere('status_id', 4);
66 |
67 | $this->assertSame('WHERE `status_id` =:status_id', $queryBuilder->getSQL());
68 |
69 | }
70 |
71 |
72 | /**
73 | * @group entity
74 | * @group db
75 | * @group db-query-builder
76 | * @group db-query-builder-insert-invoice
77 | */
78 | public function testInsertInvoice()
79 | {
80 | $data = [
81 | 'reference' => 'foo',
82 | 'total' => 1,
83 | 'vat' => 1,
84 | 'status_id' => 4,
85 | 'id' => 5
86 | ];
87 | $table = 'invoice';
88 | $sql = QueryBuilder::insert($data, $table);
89 | $expected = "INSERT INTO `invoice` (`reference`, `total`, `vat`, `status_id`, `id`) VALUE (:reference, :total, :vat, :status_id, :id);";
90 |
91 | $this->assertSame($expected, $sql);
92 | }
93 |
94 | /**
95 | * @group entity
96 | * @group db
97 | * @group db-query-builder
98 | * @group db-query-builder-insert-customer
99 | */
100 | public function testInsertCustomer()
101 | {
102 | $data = [
103 | 'first_name' => 'Peter',
104 | 'last_name' => 'Fisher',
105 | 'company_name' => 'How To Code Well',
106 | ];
107 | $table = 'customer';
108 | $sql = QueryBuilder::insert($data, $table);
109 | $expected = "INSERT INTO `customer` (`first_name`, `last_name`, `company_name`) VALUE (:first_name, :last_name, :company_name);";
110 |
111 | $this->assertSame($expected, $sql);
112 | }
113 |
114 | /**
115 | * @group entity
116 | * @group db
117 | * @group db-query-builder
118 | * @group db-query-builder-update
119 | */
120 | public function testUpdate()
121 | {
122 | $data = [
123 | 'reference' => 'reference',
124 | 'total' => 'total',
125 | 'vat' => 'vat',
126 | ];
127 |
128 | $where = [
129 | 'id' => 'id'
130 | ];
131 |
132 | $table = 'invoice';
133 |
134 | $sql = QueryBuilder::update($data, $table, $where);
135 | $expected = "UPDATE `invoice` SET `reference` =:reference, `total` =:total, `vat` =:vat WHERE `id` =:id;";
136 |
137 | $this->assertSame($expected, $sql);
138 | }
139 |
140 | /**
141 | * @group entity
142 | * @group db
143 | * @group db-query-builder
144 | * @group db-query-builder-insert-or-update-with-no-where-clause
145 | */
146 | public function testInsertOrUpdateWithNoWhereClause()
147 | {
148 | $data = [
149 | 'first_name' => 'Peter',
150 | 'last_name' => 'Fisher',
151 | 'company_name' => 'How To Code Well',
152 | ];
153 | $table = 'customer';
154 |
155 | $sql = QueryBuilder::insertOrUpdate($data, $table);
156 | $expected = "INSERT INTO `customer` (`first_name`, `last_name`, `company_name`) VALUE (:first_name, :last_name, :company_name);";
157 |
158 | $this->assertSame($expected, $sql);
159 | }
160 |
161 | /**
162 | * @group entity
163 | * @group db
164 | * @group db-query-builder
165 | * @group db-query-builder-insert-or-update-with-where-clause
166 | */
167 | public function testInsertOrUpdateWithWhereClause()
168 | {
169 | $data = [
170 | 'reference' => 'reference',
171 | 'total' => 'total',
172 | 'vat' => 'vat',
173 | ];
174 |
175 | $where = [
176 | 'id' => 'id'
177 | ];
178 |
179 | $table = 'invoice';
180 |
181 | $sql = QueryBuilder::insertOrUpdate($data, $table, $where);
182 |
183 | $expected = "UPDATE `invoice` SET `reference` =:reference, `total` =:total, `vat` =:vat WHERE `id` =:id;";
184 |
185 | $this->assertSame($expected, $sql);
186 | }
187 |
188 | /**
189 | * @group entity
190 | * @group db
191 | * @group db-query-builder
192 | * @group db-query-builder-insert-or-update-invoice-with-status
193 | */
194 | public function testInsertOrUpdateInvoiceWithStatus()
195 | {
196 | $status = new Status();
197 | $status->setInternalName('test')
198 | ->setName('test')
199 | ->setId(2);
200 | $entity = new Invoice();
201 | $entity->setReference('foo')
202 | ->setTotal(1)
203 | ->setVAT(1)
204 | ->setStatus($status);;
205 | $data = [
206 | 'reference' => $entity->getReference(),
207 | 'total' => $entity->getTotal(),
208 | 'vat' => $entity->getVat()
209 | ];
210 | if ($entity->getStatus() instanceof Status) {
211 | $data['status_id'] = $entity->getStatus()->getId();
212 | }
213 |
214 | $table = 'invoice';
215 |
216 | $sql = QueryBuilder::insertOrUpdate($data, $table);
217 |
218 | $expected = "INSERT INTO `invoice` (`reference`, `total`, `vat`, `status_id`) VALUE (:reference, :total, :vat, :status_id);";
219 |
220 | $this->assertSame($expected, $sql);
221 | }
222 |
223 | /**
224 | * @group entity
225 | * @group db
226 | * @group db-query-builder
227 | * @group db-query-builder-find-one-by-id
228 | */
229 | public function testFindOneByID()
230 | {
231 | $sql = QueryBuilder::findOneBy('status');
232 | $expected = "SELECT * FROM `status` WHERE id=:id";
233 | $this->assertSame($expected, $sql);
234 | }
235 |
236 | /**
237 | * @group entity
238 | * @group db
239 | * @group db-query-builder
240 | * @group db-query-builder-find-all
241 | */
242 | public function testFindAll()
243 | {
244 | $sql = QueryBuilder::findAll('status');
245 | $expected = "SELECT * FROM `status`";
246 | $this->assertSame($expected, $sql);
247 | }
248 |
249 | /**
250 | * @group entity
251 | * @group db
252 | * @group db-query-builder
253 | * @group db-query-builder-find-all-by
254 | */
255 | public function testFindAllBy()
256 | {
257 | $sql = QueryBuilder::findAllBy('status', [
258 | 'name' => 'name',
259 | 'internal_name' => 'internal_name'
260 | ]);
261 | $expected = "SELECT * FROM `status` WHERE `name` =:name AND `internal_name` =:internal_name";
262 | $this->assertSame($expected, $sql);
263 | }
264 |
265 | protected function _before()
266 | {
267 | }
268 |
269 | protected function _after()
270 | {
271 | }
272 |
273 | }
--------------------------------------------------------------------------------