├── .DS_Store
├── .gitattributes
├── .github
├── .DS_Store
├── dependabot.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .php-cs-fixer.cache
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── docs
├── .DS_Store
└── api
│ ├── .DS_Store
│ ├── classes
│ ├── Scrawler-Arca-Collection-CollectionInterface.html
│ ├── Scrawler-Arca-Collection.html
│ ├── Scrawler-Arca-Database.html
│ ├── Scrawler-Arca-Exception-ConstructModelException.html
│ ├── Scrawler-Arca-Exception-KeyNotFoundException.html
│ ├── Scrawler-Arca-Facade-Database.html
│ ├── Scrawler-Arca-Manager-ModelManager.html
│ ├── Scrawler-Arca-Manager-RecordManager.html
│ ├── Scrawler-Arca-Manager-TableManager.html
│ ├── Scrawler-Arca-Model.html
│ └── Scrawler-Arca-QueryBuilder.html
│ ├── css
│ ├── base.css
│ ├── normalize.css
│ └── template.css
│ ├── files
│ ├── src-collection-collectioninterface.html
│ ├── src-collection.html
│ ├── src-database.html
│ ├── src-exception-constructmodelexception.html
│ ├── src-exception-keynotfoundexception.html
│ ├── src-facade-database.html
│ ├── src-manager-modelmanager.html
│ ├── src-manager-recordmanager.html
│ ├── src-manager-tablemanager.html
│ ├── src-model.html
│ └── src-querybuilder.html
│ ├── graphs
│ └── classes.html
│ ├── index.html
│ ├── indices
│ └── files.html
│ ├── js
│ ├── search.js
│ └── searchIndex.js
│ ├── namespaces
│ ├── default.html
│ ├── scrawler-arca-collection.html
│ ├── scrawler-arca-exception.html
│ ├── scrawler-arca-facade.html
│ ├── scrawler-arca-manager.html
│ ├── scrawler-arca.html
│ └── scrawler.html
│ ├── packages
│ ├── Application.html
│ └── default.html
│ └── reports
│ ├── deprecated.html
│ ├── errors.html
│ └── markers.html
├── phpstan.neon
├── phpunit.xml
├── rector.php
├── src
├── .DS_Store
├── Collection.php
├── Collection
│ └── CollectionInterface.php
├── Config.php
├── Database.php
├── Exception
│ ├── InvalidIdException.php
│ ├── InvalidModelException.php
│ └── KeyNotFoundException.php
├── Facade
│ └── Database.php
├── Factory
│ └── DatabaseFactory.php
├── Manager
│ ├── ModelManager.php
│ ├── RecordManager.php
│ ├── TableConstraint.php
│ ├── TableManager.php
│ └── WriteManager.php
├── Model.php
├── QueryBuilder.php
└── Traits
│ └── Model
│ ├── ArrayAccess.php
│ ├── Getter.php
│ ├── Iterator.php
│ ├── Setter.php
│ └── Stringable.php
└── tests
├── Datasets
└── DatabaseTest.php
├── Pest.php
└── Unit
├── Database
├── DatabaseSaveTest.php
└── DatabaseTest.php
├── FacadeTest.php
├── Factory
└── DatabaseFactoryTest.php
├── Manager
├── ContstraintsTest.php
└── TableManagerTest.php
├── Model
├── EagerLoadingTest.php
├── ExceptionTest.php
├── ModelTest.php
├── PropertyTest.php
├── RelationsTest.php
├── TraitsTest.php
└── TypeTest.php
└── QueryBuilder
├── EagerLoadingTest.php
└── QueryBuilderTest.php
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/.DS_Store
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/.github/.DS_Store
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "composer" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request,workflow_dispatch]
3 | jobs:
4 | arca-tests:
5 | runs-on: ubuntu-latest
6 | strategy:
7 | matrix:
8 | operating-system: ['ubuntu-latest']
9 | php-versions: ['8.3']
10 | phpunit-versions: ['latest']
11 | include:
12 | - operating-system: 'ubuntu-latest'
13 | php-versions: '8.3'
14 | services:
15 | mysql:
16 | image: mysql:latest
17 | env:
18 | MYSQL_DATABASE: test_database
19 | MYSQL_HOST: 127.0.0.1
20 | MYSQL_USER: admin
21 | MYSQL_PASSWORD: rootpass
22 | MYSQL_ROOT_PASSWORD: rootpass
23 | ports:
24 | - 3306:3306
25 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
26 | steps:
27 | - uses: actions/checkout@v4
28 | - uses: shivammathur/setup-php@v2
29 | with:
30 | php-version: '8.3'
31 | - name: Update Composer
32 | run: sudo composer self-update --no-interaction
33 | - name: Run Composer Install
34 | run: composer install --no-interaction
35 | - name: run tests
36 | run: vendor/bin/pest --coverage-clover ./clover.xml
37 | - name: run static analysis
38 | run: vendor/bin/phpstan analyse src --level 8
39 | - name: Upload to Codecov
40 | uses: codecov/codecov-action@v4
41 | with:
42 | token: ${{ secrets.CODECOV_TOKEN }}
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | /vendor/
3 | .DS_Store
4 | /.phpunit.cache/
5 | index.php
6 | test_result
7 |
8 | # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
9 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
10 | # composer.lock
11 |
--------------------------------------------------------------------------------
/.php-cs-fixer.cache:
--------------------------------------------------------------------------------
1 | {"php":"8.3.17","version":"3.68.5","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":true,"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_declaration":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_hidden_params":true,"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_continue_to_break":true,"trailing_comma_in_multiline":{"after_heredoc":true,"elements":["array_destructuring","arrays","match","parameters"]},"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"array_indentation":true,"array_syntax":true,"cast_spaces":true,"new_with_parentheses":{"anonymous_class":false},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\/Collection\/CollectionInterface.php":"fb0ea7e1c3eaddde3f2a4c5d0a7f4c48","src\/Traits\/Model\/ArrayAccess.php":"d38055282a82f53ec43871d9dae354f8","src\/Traits\/Model\/Getter.php":"c735fe1562b966f84b5d14d7b09b5a33","src\/Traits\/Model\/Setter.php":"53821011d871be2bb3049f78d65a1feb","src\/Traits\/Model\/Stringable.php":"317e7fdc392d409a9e293779d0b5a18d","src\/Traits\/Model\/Iterator.php":"664118a36822949f1257774e91294191","src\/Database.php":"fb4cd8b281f3cc44dc2b90aa9b6043a5","src\/Facade\/Database.php":"495507f0bed8a896a2811c4c749d9378","src\/Model.php":"2767245e754eb11b20f2adeb1dad4a36","src\/Config.php":"38f2547f007d7a16761056941d49a47e","src\/Manager\/RecordManager.php":"eecccfce49d58b90d006913c6ce77ea5","src\/Manager\/ModelManager.php":"085351c9679ca67c699d21f2b7b7bd88","src\/Manager\/WriteManager.php":"dbe227957122d62d05528e6f1e14c386","src\/Manager\/TableManager.php":"fa424f96d11317acf249d4921967dfc5","src\/Manager\/TableConstraint.php":"dd353943f55a66802fa61090d0ade9ce","src\/Collection.php":"329ccc5ce538c94548a489a08a3d8a7e","src\/QueryBuilder.php":"78dab3298fb2d3cd7eede0456b19a61e","src\/Exception\/InvalidModelException.php":"17a384cb5d0bd5aa4e958686adcd9210","src\/Exception\/InvalidIdException.php":"1500cb4298505f8c3a7002c20b0f0df1","src\/Exception\/KeyNotFoundException.php":"07af93dc973f3cd56197717b027fffdb","src\/Factory\/DatabaseFactory.php":"6f9062a84687e5d1594b82f6f68bf730"}}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Pranjal Pandey
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
🚀 ARCA ORM
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 🔥 Low code , Zero Configuration ORM that creates models, config, database and tables on the fly. 🔥
11 | 🇮🇳 Made in India 🇮🇳
12 |
13 |
14 | 
15 |
16 | Complete documentation can be found [here](http://component.scrawlerlabs.com/arca-orm)
17 |
18 |
19 |
20 | ## 🤔 Why use Arca Orm ?
21 | - Automatically creates tables and columns as you go
22 | - No configuration, just fire and forget
23 | - Save loads of time while working on database
24 | - Built upon stable foundation of Doctrine Dbal and extensively tested
25 | - Thanks to [loophp](https://github.com/loophp/collection) Arca comes with Lazy collection and tons of helper collection functions
26 | - Supports lots database platforms , you can see the complete list [here](https://component.scrawlerlabs.com/arca-orm/database/)
27 | - Supports concurrent queries and connection pooling with swoole and async with amphp. Check out integration docs [here](https://component.scrawlerlabs.com/arca-orm/swoole/)
28 |
29 |
30 | ## ❗Requirements
31 | - PHP 8.1 or greater
32 | - PHP PDO or other supported database adapter
33 | - Mysql, MariaDB, Sqlite or any other supported database. check the list [here](https://component.scrawlerlabs.com/arca-orm/database/)
34 |
35 | ## 💻 Installation
36 | You can install Arca ORM via Composer. If you don't have composer installed , you can download composer from [here](https://getcomposer.org/download/)
37 |
38 | ```
39 | composer require scrawler/arca
40 | ```
41 |
42 |
43 | ## 🏁 QuickStart
44 |
45 | ### ✨ Setup
46 | ```php
47 | 'YOUR_DB_NAME',
52 | 'user' => 'YOUR_DB_USER',
53 | 'password' => 'YOUR_DB_PASSWORD',
54 | 'host' => 'YOUR_DB_HOST',
55 | 'driver' => 'pdo_mysql', //You can use other supported driver this is the most basic mysql driver
56 | );
57 |
58 |
59 | $db = \Scrawler\Arca\Facade\Database::connect($connectionParams);
60 |
61 | //If you dont want to use facade , directly build from factory
62 | $factory = \Scrawler\Arca\Factory\DatabaseFactory()
63 | $db = $factory->build($connectionParams)
64 |
65 | ```
66 | For complete list of driver check [here](https://component.scrawlerlabs.com/arca-orm/database/)
67 |
68 | ### ✏️ CRUD
69 | ```php
70 |
71 | // Create new record
72 | // The below code will automatically create user table and store the record
73 |
74 | $user = $db->create('user');
75 | $user->name = "Pranja Pandey";
76 | $user->age = 24
77 | $user->gender = "male"
78 | $user->save()
79 |
80 | // Get record with id 1
81 |
82 | $user = $db->getOne('user',1);
83 |
84 | //Get all records
85 |
86 | $users = $db->get('user');
87 |
88 | // Update a record
89 | $user = $db->getOne('user',1);
90 | $user->name = "Mr Pranjal";
91 | $user->save();
92 |
93 | // Delete a record
94 | $user = $db->getOne('user',1);
95 | $user->delete();
96 |
97 | ```
98 | For complete CRUD documentaion visit [here](https://component.scrawlerlabs.com/arca-orm/crud/)
99 |
100 | ### 🔎 Finding data with query
101 | ```php
102 |
103 | // Using where clause
104 | $users = $db->find('user')
105 | ->where('name = "Pranjal Pandey"')
106 | ->get();
107 | // If where input in unsafe or user defined
108 | $name = "Pranjal"
109 | $users = $db->find('user')
110 | ->where('name = ?')
111 | ->setParameter(0,$name)
112 | ->get();
113 |
114 | foreach ($users as $user){
115 | // Some logic here
116 | }
117 |
118 | // Get only single record
119 | $users = $db->find('user')
120 | ->where('name = "Pranjal Pandey"')
121 | ->first();
122 |
123 | // Using limit in query
124 | $users = $db->find('user')
125 | ->setFirstResult(10)
126 | ->setMaxResults(20);
127 | ->get()
128 |
129 | ```
130 | For complete Query documentaion visit [here](https://component.scrawlerlabs.com/arca-orm/finding/)
131 |
132 |
133 | ## 👏 Supporters
134 | If you have reached here consider giving a star to help this project ❤️
135 | [](https://github.com/scrawler-labs/arca-orm/stargazers)
136 |
137 |
138 | ## ✅ Roadmap
139 | Here is list of few things that i would like to add in upcoming release
140 | - [ ] Models should be extendible with custom models
141 | - [ ] Validations for custom models
142 | - [ ] Automatically create migrations when table is updated or created
143 | - [X] Support eager loading for relations
144 | - [X] Better documentaions
145 |
146 |
147 | ## 👍 Similar projects and inspiration
148 | - [Eloquent ORM](https://laravel.com/docs/5.0/eloquent)
149 | - [Redbean PHP](https://redbeanphp.com/index.php)
150 | - [Doctrine ORM](https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/index.html)
151 |
152 |
153 | ## 📄 License
154 | Arca ORM is created by [Pranjal Pandey](https://www.github.com/ipranjal) and released under the [Apache 2.0 License](https://github.com/scrawler-labs/arca-orm/blob/main/LICENSE).
155 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scrawler/arca",
3 | "description": "Arca ORM by Scrawler",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Pranjal Pandey",
8 | "email": "pranjal@corpusvision.com",
9 | "homepage": "https://github.com/ipranjal",
10 | "role": "Owner"
11 | }
12 | ],
13 | "autoload": {
14 | "psr-4": {
15 | "Scrawler\\Arca\\": "src/"
16 | }
17 | },
18 | "require": {
19 | "php": ">=8.1.0",
20 | "doctrine/dbal": "^4.0",
21 | "loophp/collection": "^7.5",
22 | "ramsey/uuid": "^4.3",
23 | "thecodingmachine/safe": "^3.0",
24 | "dunglas/doctrine-json-odm": "^1.4",
25 | "php-di/php-di": "^7.0"
26 | },
27 | "require-dev": {
28 | "phpstan/phpstan": "^2.0",
29 | "pestphp/pest": "^3.0",
30 | "pestphp/pest-plugin-faker": "^3.0",
31 | "thecodingmachine/phpstan-safe-rule": "^1.2",
32 | "rector/rector": "^2.0"
33 | },
34 | "config": {
35 | "allow-plugins": {
36 | "pestphp/pest-plugin": true
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/docs/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/docs/.DS_Store
--------------------------------------------------------------------------------
/docs/api/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/docs/api/.DS_Store
--------------------------------------------------------------------------------
/docs/api/css/template.css:
--------------------------------------------------------------------------------
1 | .phpdocumentor-summary {
2 | font-style: italic;
3 | }
4 | .phpdocumentor-description {
5 | margin-bottom: var(--spacing-md);
6 | }
7 | .phpdocumentor-element {
8 | position: relative;
9 | }
10 |
11 | .phpdocumentor .phpdocumentor-element__name {
12 | line-height: 1;
13 | }
14 |
15 | .phpdocumentor-element__package,
16 | .phpdocumentor-element__extends,
17 | .phpdocumentor-element__implements {
18 | display: block;
19 | font-size: var(--text-xxs);
20 | font-weight: normal;
21 | opacity: .7;
22 | }
23 |
24 | .phpdocumentor-element__package .phpdocumentor-breadcrumbs {
25 | display: inline;
26 | }
27 |
28 | .phpdocumentor-element:not(:last-child) {
29 | border-bottom: 1px solid var(--primary-color-lighten);
30 | padding-bottom: var(--spacing-lg);
31 | }
32 |
33 | .phpdocumentor-element.-deprecated .phpdocumentor-element__name {
34 | text-decoration: line-through;
35 | }
36 |
37 | .phpdocumentor-element__modifier {
38 | font-size: var(--text-xxs);
39 | padding: calc(var(--spacing-base-size) / 4) calc(var(--spacing-base-size) / 2);
40 | color: var(--text-color);
41 | background-color: var(--light-gray);
42 | border-radius: 3px;
43 | text-transform: uppercase;
44 | }
45 | .phpdocumentor-signature {
46 | display: inline-block;
47 | font-size: var(--text-sm);
48 | margin-bottom: var(--spacing-md);
49 | }
50 |
51 | .phpdocumentor-signature.-deprecated .phpdocumentor-signature__name {
52 | text-decoration: line-through;
53 | }
54 | .phpdocumentor-table-of-contents {
55 | }
56 |
57 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry {
58 | padding-top: var(--spacing-xs);
59 | margin-left: 2rem;
60 | display: flex;
61 | }
62 |
63 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry > a {
64 | flex: 0 1 auto;
65 | }
66 |
67 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry > span {
68 | flex: 1;
69 | white-space: nowrap;
70 | text-overflow: ellipsis;
71 | overflow: hidden;
72 | }
73 |
74 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry:after {
75 | content: '';
76 | height: 12px;
77 | width: 12px;
78 | left: 16px;
79 | position: absolute;
80 | }
81 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-private:after {
82 | background: url('data:image/svg+xml;utf8, ') no-repeat;
83 | }
84 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-protected:after {
85 | left: 13px;
86 | background: url('data:image/svg+xml;utf8, ') no-repeat;
87 | }
88 |
89 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry:before {
90 | width: 1.25rem;
91 | height: 1.25rem;
92 | line-height: 1.25rem;
93 | background: transparent url('data:image/svg+xml;utf8, ') no-repeat center center;
94 | content: '';
95 | position: absolute;
96 | left: 0;
97 | border-radius: 50%;
98 | font-weight: 600;
99 | color: white;
100 | text-align: center;
101 | font-size: .75rem;
102 | margin-top: .2rem;
103 | }
104 |
105 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-method:before {
106 | content: 'M';
107 | background-image: url('data:image/svg+xml;utf8, ');
108 | }
109 |
110 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-function:before {
111 | content: 'M';
112 | background-image: url('data:image/svg+xml;utf8, ');
113 | }
114 |
115 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-property:before {
116 | content: 'P'
117 | }
118 |
119 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-constant:before {
120 | content: 'C';
121 | background-color: transparent;
122 | background-image: url('data:image/svg+xml;utf8, ');
123 | }
124 |
125 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-class:before {
126 | content: 'C'
127 | }
128 |
129 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-interface:before {
130 | content: 'I'
131 | }
132 |
133 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-trait:before {
134 | content: 'T'
135 | }
136 |
137 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-namespace:before {
138 | content: 'N'
139 | }
140 |
141 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-package:before {
142 | content: 'P'
143 | }
144 |
145 | .phpdocumentor-table-of-contents .phpdocumentor-table-of-contents__entry.-enum:before {
146 | content: 'E'
147 | }
148 |
149 | .phpdocumentor-table-of-contents dd {
150 | font-style: italic;
151 | margin-left: 2rem;
152 | }
153 | .phpdocumentor-element-found-in {
154 | position: absolute;
155 | top: 0;
156 | right: 0;
157 | font-size: var(--text-sm);
158 | color: gray;
159 | }
160 |
161 | .phpdocumentor-element-found-in .phpdocumentor-element-found-in__source {
162 | flex: 0 1 auto;
163 | display: inline-flex;
164 | }
165 |
166 | .phpdocumentor-element-found-in .phpdocumentor-element-found-in__source:after {
167 | width: 1.25rem;
168 | height: 1.25rem;
169 | line-height: 1.25rem;
170 | background: transparent url('data:image/svg+xml;utf8, ') no-repeat center center;
171 | content: '';
172 | left: 0;
173 | border-radius: 50%;
174 | font-weight: 600;
175 | text-align: center;
176 | font-size: .75rem;
177 | margin-top: .2rem;
178 | }
179 | .phpdocumentor-class-graph {
180 | width: 100%; height: 600px; border:1px solid black; overflow: hidden
181 | }
182 |
183 | .phpdocumentor-class-graph__graph {
184 | width: 100%;
185 | }
186 | .phpdocumentor-tag-list__definition {
187 | display: flex;
188 | }
189 |
190 | .phpdocumentor-tag-link {
191 | margin-right: var(--spacing-sm);
192 | }
193 |
--------------------------------------------------------------------------------
/docs/api/files/src-collection.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | Collection.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | Collection
113 | Extension of LoopPHP collection
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-database.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | Database.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | Database
113 | Class that manages all interaction with database
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-facade-database.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | Database.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | Database
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-manager-modelmanager.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | ModelManager.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | ModelManager
113 | Class for initializing and managing models
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-manager-tablemanager.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | TableManager.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | TableManager
113 | Class resposible for creating and modifing table
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-model.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | Model.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | Model
113 | Model class that represents single record in database
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/files/src-querybuilder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | QueryBuilder.php
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Interfaces, Classes, Traits and Enums
107 |
108 |
109 |
110 |
111 |
112 | QueryBuilder
113 | Extended implementation of \Doctrine\DBAL\Query\QueryBuilder
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/docs/api/graphs/classes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
37 |
38 |
39 |
40 |
41 |
44 |
78 |
79 |
80 |
81 |
82 |
83 |
91 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/docs/api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
Documentation
92 |
93 |
94 |
95 | Packages
96 |
97 |
98 |
99 |
100 | Application
101 |
102 |
103 |
104 | Namespaces
105 |
106 |
107 |
108 |
109 | Scrawler
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/docs/api/namespaces/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | API Documentation
96 |
97 |
98 |
99 | Namespaces
100 |
101 |
102 |
103 |
104 | Scrawler
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/docs/api/namespaces/scrawler.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | Scrawler
96 |
97 |
98 |
99 | Namespaces
100 |
101 |
102 |
103 |
104 | Arca
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/docs/api/packages/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
48 |
49 |
50 |
51 |
52 |
55 |
89 |
90 |
91 |
93 |
94 |
95 | API Documentation
96 |
97 |
98 | Packages
99 |
100 |
101 |
102 |
103 | Application
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/docs/api/reports/deprecated.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation » Deprecated elements
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
49 |
50 |
51 |
52 |
53 |
56 |
90 |
91 |
92 |
95 |
96 |
97 |
Deprecated
98 |
99 |
100 |
101 | No deprecated elements have been found in this project.
102 |
103 |
104 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/docs/api/reports/errors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation » Compilation errors
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
49 |
50 |
51 |
52 |
53 |
56 |
90 |
91 |
92 |
95 |
96 |
97 |
Errors
98 |
99 |
100 |
No errors have been found in this project.
101 |
102 |
103 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/docs/api/reports/markers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Documentation » Markers
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
49 |
50 |
51 |
52 |
53 |
56 |
90 |
91 |
92 |
95 |
96 |
97 |
Markers
98 |
99 |
100 | No markers have been found in this project.
101 |
102 |
103 |
104 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - phar://phpstan.phar/conf/bleedingEdge.neon
3 | - vendor/thecodingmachine/phpstan-safe-rule/phpstan-safe-rule.neon
4 |
5 | parameters:
6 | ignoreErrors:
7 | -
8 | identifier: missingType.generics
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests
6 |
7 |
8 |
9 |
10 |
11 | ./app
12 | ./src
13 |
14 |
15 | ./src/Collection.php
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths([
9 | __DIR__ . '/src',
10 | __DIR__ . '/tests',
11 | ])
12 | // uncomment to reach your current PHP version
13 | ->withPhpSets(php83:true)
14 | ->withAttributesSets()
15 | ->withPreparedSets(deadCode:true,typeDeclarations:true,codeQuality:true);
16 |
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/src/.DS_Store
--------------------------------------------------------------------------------
/src/Collection.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca;
13 |
14 | use loophp\collection\Collection as LoopCollection;
15 | use loophp\collection\Contract\Collection as LoopCollectionInterface;
16 | use Scrawler\Arca\Collection\CollectionInterface;
17 |
18 | /**
19 | * Extension of LoopPHP collection.
20 | *
21 | * @template TKey
22 | * @template T
23 | */
24 | final class Collection implements CollectionInterface
25 | {
26 | /**
27 | * @var LoopCollectionInterface
28 | */
29 | private readonly LoopCollectionInterface $collection;
30 |
31 | private function __construct(?LoopCollectionInterface $collection = null)
32 | {
33 | $this->collection = $collection ?? LoopCollection::empty();
34 | }
35 |
36 | /**
37 | * /**
38 | *
39 | * @template UKey
40 | * @template U
41 | *
42 | * @param iterable $iterable
43 | */
44 | public static function fromIterable(iterable $iterable): self
45 | {
46 | return new self(LoopCollection::fromIterable($iterable));
47 | }
48 |
49 | /**
50 | * @return array
51 | */
52 | public function jsonSerialize(): array
53 | {
54 | return $this->all(false);
55 | }
56 |
57 | public function __toString(): string
58 | {
59 | return $this->toString();
60 | }
61 |
62 | /**
63 | * @return array
64 | */
65 | public function toArray(): array
66 | {
67 | $toArray = static fn ($val) => $val->toArray();
68 |
69 | return $this->map($toArray)->jsonSerialize();
70 | }
71 |
72 | public function toString(): string
73 | {
74 | return \Safe\json_encode($this->toArray());
75 | }
76 |
77 | /**
78 | * @return array
79 | */
80 | public function all(bool $normalize = false): array
81 | {
82 | return $this->collection->all($normalize);
83 | }
84 |
85 | public function apply(callable $callbacks): Collection
86 | {
87 | return new self($this->collection->apply($callbacks));
88 | }
89 |
90 | /**
91 | * @param mixed[] $other
92 | */
93 | public function equals(iterable $other): bool
94 | {
95 | return $this->collection->equals($other);
96 | }
97 |
98 | public function first(): ?Model
99 | {
100 | return $this->collection->first();
101 | }
102 |
103 | public function init(): Collection
104 | {
105 | return new self($this->collection->init());
106 | }
107 |
108 | public function inits(): Collection
109 | {
110 | return new self($this->collection->inits());
111 | }
112 |
113 | /**
114 | * @param array|\Traversable $sources
115 | */
116 | public function merge(iterable ...$sources): Collection
117 | {
118 | return new self($this->collection->merge(...$sources));
119 | }
120 |
121 | public function map(callable $callback): Collection
122 | {
123 | return new self($this->collection->map($callback));
124 | }
125 |
126 | /**
127 | * @param callable(T, TKey, iterable): bool ...$callbacks
128 | */
129 | public function filter(callable ...$callbacks): Collection
130 | {
131 | return new self($this->collection->filter(...$callbacks));
132 | }
133 |
134 | public function count(): int
135 | {
136 | return iterator_count(iterator: $this);
137 | }
138 |
139 | /**
140 | * @param callable(T, TKey, iterable): bool ...$callbacks
141 | */
142 | public function find(mixed $default = null, callable ...$callbacks): mixed
143 | {
144 | return $this->collection->find($default, ...$callbacks);
145 | }
146 |
147 | public function limit(int $count = -1, int $offset = 0): Collection
148 | {
149 | return new self($this->collection->limit($count, $offset));
150 | }
151 |
152 | public function last(): ?Model
153 | {
154 | return $this->collection->last();
155 | }
156 |
157 | public function getIterator(): \Iterator
158 | {
159 | yield from $this->collection->getIterator();
160 | }
161 |
162 | public function current(int $index = 0, mixed $default = null): mixed
163 | {
164 | return $this->collection->current($index, $default);
165 | }
166 |
167 | /**
168 | * @return TKey|null
169 | */
170 | public function key(int $index = 0)
171 | {
172 | return $this->collection->key($index);
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/src/Collection/CollectionInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Collection;
13 |
14 | /**
15 | * @template TKey
16 | * @template T
17 | */
18 | interface CollectionInterface extends \IteratorAggregate, \Countable
19 | {
20 | public function __toString(): string;
21 |
22 | /**
23 | * @return array
24 | */
25 | public function toArray(): array;
26 |
27 | public function toString(): string;
28 |
29 | public function getIterator(): \Iterator;
30 |
31 | public function apply(callable $callables): CollectionInterface;
32 |
33 | public function map(callable $callable): CollectionInterface;
34 |
35 | /**
36 | * @param mixed[] $array
37 | */
38 | public static function fromIterable(iterable $array): CollectionInterface;
39 |
40 | /**
41 | * @return array
42 | */
43 | public function jsonSerialize(): array;
44 | }
45 |
--------------------------------------------------------------------------------
/src/Config.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca;
13 |
14 | /**
15 | * Class to store the configuration.
16 | */
17 | class Config
18 | {
19 | public function __construct(
20 | private readonly bool $isUUID = false,
21 | private bool $isFrozen = false)
22 | {
23 | }
24 |
25 | /**
26 | * Get if the connection is using UUID.
27 | */
28 | public function isUsingUUID(): bool
29 | {
30 | return $this->isUUID;
31 | }
32 |
33 | /**
34 | * Set if the connection is frozen.
35 | */
36 | public function setFrozen(bool $isFrozen): void
37 | {
38 | $this->isFrozen = $isFrozen;
39 | }
40 |
41 | /**
42 | * Get if the connection is frozen.
43 | */
44 | public function isFrozen(): bool
45 | {
46 | return $this->isFrozen;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Database.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Scrawler\Arca;
15 |
16 | use Doctrine\DBAL\Connection;
17 | use Doctrine\DBAL\Types\Type;
18 | use Dunglas\DoctrineJsonOdm\Serializer;
19 | use Dunglas\DoctrineJsonOdm\Type\JsonDocumentType;
20 | use Scrawler\Arca\Manager\ModelManager;
21 | use Scrawler\Arca\Manager\RecordManager;
22 | use Scrawler\Arca\Manager\WriteManager;
23 | use Symfony\Component\Serializer\Encoder\JsonEncoder;
24 | use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
25 | use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
26 | use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
27 | use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
28 | use Symfony\Component\Serializer\Normalizer\UidNormalizer;
29 |
30 | /**
31 | * Class that manages all interaction with database.
32 | */
33 | final class Database
34 | {
35 | /**
36 | * Create a new Database instance.
37 | */
38 | public function __construct(
39 | private readonly Connection $connection,
40 | private readonly RecordManager $recordManager,
41 | private readonly WriteManager $writeManager,
42 | private readonly ModelManager $modelManager,
43 | private readonly Config $config,
44 | ) {
45 | $this->registerJsonDocumentType();
46 | }
47 |
48 | /**
49 | * Executes an SQL query and returns the number of row affected.
50 | *
51 | * @param array $params
52 | *
53 | * @return int|numeric-string
54 | */
55 | public function exec(string $sql, array $params = []): int|string
56 | {
57 | return $this->connection->executeStatement($sql, $params);
58 | }
59 |
60 | /**
61 | * Returns array of data from SQL select statement.
62 | *
63 | * @param array $params
64 | *
65 | * @return array>
66 | */
67 | public function getAll(string $sql, array $params = []): array
68 | {
69 | return $this->connection->executeQuery($sql, $params)->fetchAllAssociative();
70 | }
71 |
72 | /**
73 | * Creates model from name.
74 | */
75 | public function create(string $name): Model
76 | {
77 | return $this->modelManager->create($name);
78 | }
79 |
80 | /**
81 | * Save record to database.
82 | */
83 | public function save(Model $model): mixed
84 | {
85 | return $this->writeManager->save($model);
86 | }
87 |
88 | /**
89 | * Delete record from database.
90 | */
91 | public function delete(Model $model): mixed
92 | {
93 | return $this->recordManager->delete($model);
94 | }
95 |
96 | /**
97 | * Get collection of all records from table.
98 | */
99 | public function get(string $table): Collection
100 | {
101 | return $this->recordManager->getAll($table);
102 | }
103 |
104 | /**
105 | * Get single record.
106 | */
107 | public function getOne(string $table, mixed $id): ?Model
108 | {
109 | return $this->recordManager->getById($table, $id);
110 | }
111 |
112 | /**
113 | * Returns QueryBuilder to build query for finding data
114 | * Eg: db()->find('user')->where('active = 1')->get();.
115 | */
116 | public function find(string $name): QueryBuilder
117 | {
118 | return $this->recordManager->find($name);
119 | }
120 |
121 | /**
122 | * Returns QueryBuilder to build query for finding data
123 | * Eg: db()->select('*')->from('user')->where('active = 1')->get();.
124 | */
125 | public function select(string $expression): QueryBuilder
126 | {
127 | return $this->recordManager->select($expression);
128 | }
129 |
130 | /**
131 | * Freezes table for production.
132 | */
133 | public function freeze(): void
134 | {
135 | $this->config->setFrozen(true);
136 | }
137 |
138 | /**
139 | * Helper function to unfreeze table.
140 | */
141 | public function unfreeze(): void
142 | {
143 | $this->config->setFrozen(false);
144 | }
145 |
146 | /**
147 | * Checks if database is currently using uuid rather than id.
148 | */
149 | public function isUsingUUID(): bool
150 | {
151 | return $this->config->isUsingUUID();
152 | }
153 |
154 | /**
155 | * Returns the current connection.
156 | */
157 | public function getConnection(): Connection
158 | {
159 | return $this->connection;
160 | }
161 |
162 | /**
163 | * Check if tables exist.
164 | *
165 | * @param array $tables
166 | */
167 | public function tablesExist(array $tables): bool
168 | {
169 | return $this->connection
170 | ->createSchemaManager()
171 | ->tablesExist($tables);
172 | }
173 |
174 | /**
175 | * Check if table exists.
176 | */
177 | public function tableExists(string $table): bool
178 | {
179 | return $this->connection
180 | ->createSchemaManager()
181 | ->tableExists($table);
182 | }
183 |
184 | /**
185 | * Register additional json_document type.
186 | * Note: storing and retrival of array is being tested
187 | * so ignoring this in coverage.
188 | */
189 | private function registerJsonDocumentType(): void
190 | {
191 | // @codeCoverageIgnoreStart
192 | if (!Type::hasType('json_document')) {
193 | Type::addType('json_document', JsonDocumentType::class);
194 | // @phpstan-ignore-next-line
195 | Type::getType('json_document')->setSerializer(
196 | new Serializer([new BackedEnumNormalizer(), new UidNormalizer(), new DateTimeNormalizer(), new ArrayDenormalizer(), new ObjectNormalizer()], [new JsonEncoder()])
197 | );
198 | }
199 | // @codeCoverageIgnoreEnd
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/Exception/InvalidIdException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Exception;
13 |
14 | class InvalidIdException extends \Exception
15 | {
16 | public function __construct()
17 | {
18 | parent::__construct('Force setting of id for model is not allowed');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Exception/InvalidModelException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Exception;
13 |
14 | class InvalidModelException extends \Exception
15 | {
16 | public function __construct()
17 | {
18 | parent::__construct("parameter passed to shared list or own list should be array of class \Arca\Model");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Exception/KeyNotFoundException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Exception;
13 |
14 | class KeyNotFoundException extends \Exception
15 | {
16 | public function __construct()
17 | {
18 | parent::__construct('Key you are trying to access does not exist');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Facade/Database.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Scrawler\Arca\Facade;
15 |
16 | use Scrawler\Arca\Collection;
17 | use Scrawler\Arca\Connection;
18 | use Scrawler\Arca\Database as DB;
19 | use Scrawler\Arca\Factory\DatabaseFactory;
20 | use Scrawler\Arca\Model;
21 | use Scrawler\Arca\QueryBuilder;
22 |
23 | class Database
24 | {
25 | /**
26 | * Store the instance of current connection.
27 | */
28 | private static DB $database;
29 |
30 | /**
31 | * Create a new Database instance.
32 | *
33 | * @param array $connectionParams
34 | */
35 | public static function connect(array $connectionParams): DB
36 | {
37 | $factory = new DatabaseFactory();
38 | self::$database = $factory->build($connectionParams);
39 |
40 | return self::$database;
41 | }
42 |
43 | /**
44 | * Get the instance of current connection.
45 | */
46 | private static function getDB(): DB
47 | {
48 | return self::$database;
49 | }
50 |
51 | /**
52 | * Create a new model.
53 | */
54 | public static function create(string $name): Model
55 | {
56 | return self::getDB()->create($name);
57 | }
58 |
59 | /**
60 | * Save a model.
61 | */
62 | public static function get(string $table): Collection
63 | {
64 | return self::getDB()->get($table);
65 | }
66 |
67 | /**
68 | * Save a model.
69 | */
70 | public static function getOne(string $table, mixed $id): ?Model
71 | {
72 | return self::getDB()->getOne($table, $id);
73 | }
74 |
75 | /**
76 | * Execure a raw sql query.
77 | *
78 | * @return int|numeric-string
79 | */
80 | public static function exec(string $sql): int|string
81 | {
82 | return self::getDB()->exec($sql);
83 | }
84 |
85 | /**
86 | * Delete a model.
87 | */
88 | public static function delete(Model $model): mixed
89 | {
90 | return self::getDB()->delete($model);
91 | }
92 |
93 | /**
94 | * QUery builder to find a model.
95 | */
96 | public static function find(string $table): QueryBuilder
97 | {
98 | return self::getDB()->find($table);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Factory/DatabaseFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Factory;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\DriverManager;
16 | use Scrawler\Arca\Config;
17 | use Scrawler\Arca\Database;
18 | use Scrawler\Arca\Manager\ModelManager;
19 |
20 | class DatabaseFactory
21 | {
22 | private readonly \DI\Container $container;
23 |
24 | public function __construct(?\DI\Container $container = null)
25 | {
26 | $this->container = is_null($container) ? new \DI\Container() : $container;
27 | }
28 |
29 | /**
30 | * Create a new Database instance.
31 | *
32 | * @param array $connectionParams
33 | */
34 | public function build(array $connectionParams): Database
35 | {
36 | $this->wireContainer($connectionParams);
37 |
38 | return $this->container->make(Database::class);
39 | }
40 |
41 | /**
42 | * Create a new Database instance.
43 | *
44 | * @param array $connectionParams
45 | */
46 | public function wireContainer(array $connectionParams): void
47 | {
48 | $useUUID = $connectionParams['useUUID'] ?? false;
49 |
50 | $this->createConfig($useUUID);
51 | unset($connectionParams['useUUID']);
52 | $this->createConnection($connectionParams);
53 | $this->createModelManager();
54 | }
55 |
56 | /**
57 | * Create a new connection.
58 | *
59 | * @param array $connectionParams
60 | */
61 | private function createConnection(array $connectionParams): void
62 | {
63 | $this->container->set(Connection::class, fn (): Connection => DriverManager::getConnection($connectionParams));
64 | }
65 |
66 | private function createModelManager(): void
67 | {
68 | $this->container->set(ModelManager::class, fn (): ModelManager => new ModelManager($this->container));
69 | }
70 |
71 | private function createConfig(bool $useUUID): void
72 | {
73 | $this->container->set(Config::class, fn (): Config => new Config($useUUID));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Manager/ModelManager.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace Scrawler\Arca\Manager;
15 |
16 | use Scrawler\Arca\Model;
17 |
18 | /**
19 | * Class for initializing and managing models.
20 | */
21 | class ModelManager
22 | {
23 | public function __construct(private readonly \DI\Container $container)
24 | {
25 | }
26 |
27 | /**
28 | * Create a new model.
29 | */
30 | public function create(string $name): Model
31 | {
32 | return $this->container->make(Model::class, ['table' => $name]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Manager/RecordManager.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Manager;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Ramsey\Uuid\Uuid;
16 | use Scrawler\Arca\Collection;
17 | use Scrawler\Arca\Config;
18 | use Scrawler\Arca\Model;
19 | use Scrawler\Arca\QueryBuilder;
20 |
21 | /**
22 | * Class responsible for manging single records.
23 | */
24 | final class RecordManager
25 | {
26 | /**
27 | * Create RecordManager.
28 | */
29 | public function __construct(
30 | private readonly Connection $connection,
31 | private readonly ModelManager $modelManager,
32 | private readonly Config $config,
33 | ) {
34 | }
35 |
36 | /**
37 | * Create a new record.
38 | */
39 | public function insert(Model $model): mixed
40 | {
41 | if ($this->config->isUsingUUID()) {
42 | $model->set('id', Uuid::uuid4()->toString());
43 | }
44 | $this->connection->insert($model->getName(), $model->getSelfProperties());
45 | if ($this->config->isUsingUUID()) {
46 | return $model->get('id');
47 | }
48 |
49 | return (int) $this->connection->lastInsertId();
50 | }
51 |
52 | /**
53 | * Update a record.
54 | */
55 | public function update(Model $model): mixed
56 | {
57 | $this->connection->update($model->getName(), $model->getSelfProperties(), ['id' => $model->getId()]);
58 |
59 | return $model->getId();
60 | }
61 |
62 | /**
63 | * Delete a record.
64 | */
65 | public function delete(Model $model): mixed
66 | {
67 | $this->connection->delete($model->getName(), ['id' => $model->getId()]);
68 |
69 | return $model->getId();
70 | }
71 |
72 | /**
73 | * Get single record by id.
74 | */
75 | public function getById(string $table, mixed $id): ?Model
76 | {
77 | $query = (new QueryBuilder($this->connection, $this->modelManager))
78 | ->select('*')
79 | ->from($table, 't')
80 | ->where('t.id = ?')
81 | ->setParameter(0, $id);
82 |
83 | return $query->first();
84 | }
85 |
86 | /**
87 | * Get all records.
88 | */
89 | public function getAll(string $tableName): Collection
90 | {
91 | return (new QueryBuilder($this->connection, $this->modelManager))
92 | ->select('*')
93 | ->from($tableName, 't')
94 | ->get();
95 | }
96 |
97 | /**
98 | * get query builder from db.
99 | */
100 | public function find(string $name): QueryBuilder
101 | {
102 | return (new QueryBuilder($this->connection, $this->modelManager))
103 | ->select('*')
104 | ->from($name, 't');
105 | }
106 |
107 | /**
108 | * get query builder from db.
109 | */
110 | public function select(string $expression): QueryBuilder
111 | {
112 | return (new QueryBuilder($this->connection, $this->modelManager))
113 | ->select($expression);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Manager/TableConstraint.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Manager;
13 |
14 | /**
15 | * Class to store table constraints.
16 | */
17 | class TableConstraint
18 | {
19 | public function __construct(
20 | private readonly string $foreignTableName,
21 | private readonly string $localColumnName,
22 | private readonly string $foreignColumnName,
23 | ) {
24 | }
25 |
26 | public function getForeignTableName(): string
27 | {
28 | return $this->foreignTableName;
29 | }
30 |
31 | public function getLocalColumnName(): string
32 | {
33 | return $this->localColumnName;
34 | }
35 |
36 | public function getForeignColumnName(): string
37 | {
38 | return $this->foreignColumnName;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Manager/TableManager.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Manager;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\Schema\AbstractSchemaManager;
16 | use Doctrine\DBAL\Schema\Exception\TableDoesNotExist;
17 | use Doctrine\DBAL\Schema\Schema;
18 | use Doctrine\DBAL\Schema\Table;
19 | use Doctrine\DBAL\Types\Type;
20 | use Scrawler\Arca\Config;
21 | use Scrawler\Arca\Model;
22 |
23 | /**
24 | * Class resposible for creating and modifing table.
25 | */
26 | final class TableManager
27 | {
28 | /**
29 | * Store the instance of SchemaManager.
30 | */
31 | private readonly AbstractSchemaManager $manager;
32 |
33 | /**
34 | * Store the instance of Platform.
35 | */
36 | private readonly \Doctrine\DBAL\Platforms\AbstractPlatform $platform;
37 |
38 | /**
39 | * create TableManager.
40 | */
41 | public function __construct(
42 | private readonly Connection $connection,
43 | private readonly Config $config,
44 | ) {
45 | $this->manager = $this->connection->createSchemaManager();
46 | $this->platform = $this->connection->getDatabasePlatform();
47 | }
48 |
49 | /**
50 | * Creates a table schema from table instance.
51 | */
52 | public function createTableSchema(Table $table): Schema
53 | {
54 | return new Schema([$table]);
55 | }
56 |
57 | /**
58 | * Get Table schema from existing table.
59 | */
60 | public function getTableSchema(string $table): Schema
61 | {
62 | return new Schema([$this->manager->introspectTable($table)]);
63 | }
64 |
65 | /**
66 | * Get Table detail from existing table.
67 | */
68 | public function getTable(string $table): Table
69 | {
70 | return $this->manager->introspectTable($table);
71 | }
72 |
73 | /**
74 | * Create table from model.
75 | *
76 | * @param array $constraints
77 | */
78 | public function createTable(Model $model, array $constraints = []): Table
79 | {
80 | $table = new Table($model->getName());
81 | $table = $this->addPrimaryKey($table);
82 | $types = $model->getTypes();
83 | foreach (array_keys($model->getSelfProperties()) as $key) {
84 | $key_array = explode('_', $key);
85 | if ('id' != $key && 'id' === end($key_array)) {
86 | $table = $this->addIdColumn($table, $key);
87 | } elseif ('id' != $key) {
88 | $table->addColumn(
89 | $key,
90 | $types[$key],
91 | ['notnull' => false, 'comment' => $types[$key]]
92 | );
93 | }
94 | }
95 |
96 | foreach ($constraints as $constraint) {
97 | $table->addForeignKeyConstraint(
98 | $constraint->getForeignTableName(),
99 | [$constraint->getLocalColumnName()],
100 | [$constraint->getForeignColumnName()],
101 | ['onUpdate' => 'CASCADE', 'onDelete' => 'CASCADE']
102 | );
103 | }
104 |
105 | return $table;
106 | }
107 |
108 | private function addPrimaryKey(Table $table): Table
109 | {
110 | if ($this->config->isUsingUUID()) {
111 | $table->addColumn(
112 | 'id',
113 | 'string',
114 | ['length' => 36, 'notnull' => true, 'comment' => 'string']
115 | );
116 | } else {
117 | $table->addColumn(
118 | 'id',
119 | 'integer',
120 | ['unsigned' => true, 'autoincrement' => true, 'comment' => 'integer']
121 | );
122 | }
123 | $table->setPrimaryKey(['id']);
124 |
125 | return $table;
126 | }
127 |
128 | /**
129 | * Add id column to table.
130 | */
131 | private function addIdColumn(Table $table, string $key): Table
132 | {
133 | if ($this->config->isUsingUUID()) {
134 | $table->addColumn(
135 | $key,
136 | 'string',
137 | ['length' => 36, 'notnull' => true, 'comment' => 'string']
138 | );
139 | } else {
140 | $table->addColumn(
141 | $key,
142 | 'integer',
143 | ['unsigned' => true, 'notnull' => true, 'comment' => 'integer']
144 | );
145 | }
146 |
147 | return $table;
148 | }
149 |
150 | /**
151 | * Save table to database.
152 | */
153 | public function saveTable(Table $table): void
154 | {
155 | $schema = $this->createTableSchema($table);
156 | $queries = $schema->toSql($this->platform);
157 | foreach ($queries as $query) {
158 | $this->connection->executeQuery($query);
159 | }
160 | }
161 |
162 | /**
163 | * Add missing column to existing table from given table.
164 | */
165 | public function updateTable(string $table_name, Table $new_table): void
166 | {
167 | $comparator = $this->manager->createComparator();
168 | $old_table = $this->getTable($table_name);
169 | $old_schema = $this->getTableSchema($table_name);
170 |
171 | $tableDiff = $comparator->compareTables($old_table, $new_table);
172 | $mod_table = $old_table;
173 | foreach ($tableDiff->getAddedColumns() as $column) {
174 | $mod_table->addColumn(
175 | $column->getName(),
176 | Type::getTypeRegistry()->lookupName($column->getType()),
177 | [
178 | 'notnull' => $column->getNotnull(),
179 | 'comment' => $column->getComment(),
180 | 'default' => $column->getDefault(),
181 | ]
182 | );
183 | }
184 | $new_schema = $this->createTableSchema($mod_table);
185 | $schemaDiff = $comparator->compareSchemas($old_schema, $new_schema);
186 |
187 | $queries = $this->platform->getAlterSchemaSQL($schemaDiff);
188 |
189 | foreach ($queries as $query) {
190 | $this->connection->executeQuery($query);
191 | }
192 | }
193 |
194 | /**
195 | * Check if table exists.
196 | */
197 | public function tableExists(string $table_name): bool
198 | {
199 | try {
200 | $this->getTable($table_name);
201 |
202 | return true;
203 | } catch (TableDoesNotExist) {
204 | return false;
205 | }
206 | }
207 |
208 | /**
209 | * If table exist update it else create it.
210 | */
211 | public function saveOrUpdateTable(string $table_name, Table $new_table): void
212 | {
213 | if ($this->tableExists($table_name)) {
214 | $this->updateTable($table_name, $new_table);
215 |
216 | return;
217 | }
218 |
219 | $this->saveTable($new_table);
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/src/QueryBuilder.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder;
16 | use Doctrine\DBAL\Schema\AbstractSchemaManager;
17 | use Scrawler\Arca\Manager\ModelManager;
18 |
19 | /**
20 | * Extended implementation of \Doctrine\DBAL\Query\QueryBuilder.
21 | */
22 | final class QueryBuilder extends DoctrineQueryBuilder
23 | {
24 | private string $table;
25 | /**
26 | * @var array
27 | */
28 | private array $relations = [];
29 |
30 | private readonly AbstractSchemaManager $SchemaManager;
31 |
32 | public function __construct(
33 | Connection $connection,
34 | private readonly ModelManager $modelManager,
35 | ) {
36 | $this->SchemaManager = $connection->createSchemaManager();
37 | parent::__construct($connection);
38 | }
39 |
40 | public function with(string $relation): QueryBuilder
41 | {
42 | $this->relations[] = $relation;
43 |
44 | return $this;
45 | }
46 |
47 | public function from(string $table, ?string $alias = null): QueryBuilder
48 | {
49 | $this->table = $table;
50 |
51 | return parent::from($table, $alias);
52 | }
53 |
54 | public function get(): Collection
55 | {
56 | if (!$this->SchemaManager->tableExists($this->table)) {
57 | return Collection::fromIterable([]);
58 | }
59 | $model = $this->modelManager->create($this->table);
60 | $relations = $this->relations;
61 | $this->relations = [];
62 | $results = $this->fetchAllAssociative();
63 |
64 | return Collection::fromIterable($results)
65 | ->map(static fn ($value): Model => $model->setLoadedProperties($value)->with($relations)->setLoaded());
66 | }
67 |
68 | public function first(): ?Model
69 | {
70 | if (!$this->SchemaManager->tableExists($this->table)) {
71 | return null;
72 | }
73 | $relations = $this->relations;
74 | $this->relations = [];
75 | $result = $this->fetchAssociative() ?: [];
76 | if ([] === $result) {
77 | return null;
78 | }
79 |
80 | return $this->modelManager->create($this->table)->setLoadedProperties($result)->with($relations)->setLoaded();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Traits/Model/ArrayAccess.php:
--------------------------------------------------------------------------------
1 | set($offset, $value);
13 | }
14 | }
15 |
16 | public function offsetExists($offset): bool
17 | {
18 | return $this->isset($offset);
19 | }
20 |
21 | public function offsetUnset($offset): void
22 | {
23 | $this->unset($offset);
24 | }
25 |
26 | public function offsetGet($offset): mixed
27 | {
28 | return $this->get($offset);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Traits/Model/Getter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Traits\Model;
13 |
14 | use Scrawler\Arca\Collection;
15 |
16 | /**
17 | * Getter trait to provide getter methods to the model.
18 | */
19 | trait Getter
20 | {
21 | /**
22 | * Get current model Id or UUID.
23 | */
24 | public function getId(): mixed
25 | {
26 | return $this->__meta['id'];
27 | }
28 |
29 | /**
30 | * Get current table name of model.
31 | */
32 | public function getName(): string
33 | {
34 | return $this->table;
35 | }
36 |
37 | /**
38 | * Get all properties with relational models in array form.
39 | *
40 | * @return array
41 | */
42 | public function getProperties(): array
43 | {
44 | return $this->__properties['all'];
45 | }
46 |
47 | /**
48 | * Get self properties without relations in array form.
49 | *
50 | * @return array
51 | */
52 | public function getSelfProperties(): array
53 | {
54 | return $this->__properties['self'];
55 | }
56 |
57 | /**
58 | * Get all property types in array form.
59 | *
60 | * @return array
61 | */
62 | public function getTypes(): array
63 | {
64 | return $this->__properties['type'];
65 | }
66 |
67 | /**
68 | * returns all relational models.
69 | */
70 | public function getForeignModels(string $type): Collection
71 | {
72 | return is_null($this->__meta['foreign_models'][$type]) ? Collection::fromIterable([]) : $this->__meta['foreign_models'][$type];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Traits/Model/Iterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Traits\Model;
13 |
14 | use Scrawler\Arca\Collection;
15 | use Scrawler\Arca\Model;
16 |
17 | /**
18 | * Iterator trait to provide iterator methods to the model.
19 | */
20 | trait Iterator
21 | {
22 | /**
23 | * Get all properties in array form.
24 | *
25 | * @return array
26 | */
27 | public function toArray(): array
28 | {
29 | $props = $this->getProperties();
30 | foreach ($props as $key => $value) {
31 | if ($value instanceof Model) {
32 | $props[$key] = $value->toArray();
33 | }
34 | if ($value instanceof Collection) {
35 | $props[$key] = $value->toArray();
36 | }
37 | }
38 |
39 | return $props;
40 | }
41 |
42 | public function getIterator(): \Traversable
43 | {
44 | return new \ArrayIterator($this->toArray());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Traits/Model/Setter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Traits\Model;
13 |
14 | use Doctrine\DBAL\Types\Type;
15 | use Scrawler\Arca\Model;
16 |
17 | /**
18 | * Setter trait to provide setter methods to the model.
19 | */
20 | trait Setter
21 | {
22 | /**
23 | * Set all properties of model via array.
24 | *
25 | * @param array $properties
26 | */
27 | public function setProperties(array $properties): Model
28 | {
29 | foreach ($properties as $key => $value) {
30 | $this->set($key, $value);
31 | }
32 |
33 | return $this;
34 | }
35 |
36 | /**
37 | * Set all properties of model loaded via database.
38 | *
39 | * @param array $properties
40 | */
41 | public function setLoadedProperties(array $properties): Model
42 | {
43 | $this->__properties['self'] = $properties;
44 | $this->setLoadedAllProperties($properties);
45 | foreach (array_keys($properties) as $key) {
46 | $this->__properties['type'][$key] = $this->tableManager->getTable($this->table)->getColumn($key)->getComment();
47 | }
48 | $this->__meta['id'] = $properties['id'];
49 |
50 | return $this;
51 | }
52 |
53 | /**
54 | * get php value from database value.
55 | *
56 | * @param array $properties
57 | */
58 | private function setLoadedAllProperties(array $properties): void
59 | {
60 | foreach ($properties as $key => $value) {
61 | $type = Type::getType($this->tableManager->getTable($this->table)->getColumn($key)->getComment());
62 | $this->__properties['all'][$key] = $type->convertToPHPValue($value, $this->connection->getDatabasePlatform());
63 | }
64 | }
65 |
66 | /**
67 | * call when model is loaded from database.
68 | */
69 | public function setLoaded(): Model
70 | {
71 | $this->__meta['is_loaded'] = true;
72 | $this->__meta['id_error'] = false;
73 |
74 | return $this;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Traits/Model/Stringable.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Scrawler\Arca\Traits\Model;
13 |
14 | /**
15 | * Stringable trait to provide string conversion methods to the model.
16 | */
17 | trait Stringable
18 | {
19 | /**
20 | * Converts model into json object.
21 | */
22 | public function toString(): string
23 | {
24 | return \Safe\json_encode($this->toArray());
25 | }
26 |
27 | /**
28 | * Converts model into json object.
29 | */
30 | public function __toString(): string
31 | {
32 | return $this->toString();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/Datasets/DatabaseTest.php:
--------------------------------------------------------------------------------
1 | 'test_database',
23 | 'user' => 'admin',
24 | 'password' => 'rootpass',
25 | 'host' => '127.0.0.1',
26 | 'driver' => 'pdo_mysql',
27 | ];
28 | if ($withUUID) {
29 | $config['useUUID'] = 'UUID' == $uuid ? true : false;
30 | }
31 |
32 | return $config;
33 | }
34 |
35 | function populateRandomUser($uuid = 'ID'): void
36 | {
37 | for ($i = 0; $i < 5; ++$i) {
38 | $user = db($uuid)->create('user');
39 | $user->name = fake()->name();
40 | $user->email = fake()->email();
41 | $user->dob = fake()->date();
42 | $user->age = fake()->randomNumber(2, false);
43 | $user->active = $i % 2;
44 | $user->address = fake()->streetAddress();
45 | $user->save();
46 | }
47 | }
48 |
49 | function createRandomUser($uuid = 'ID')
50 | {
51 | $user = db($uuid)->create('user');
52 | $user->name = fake()->name();
53 | $user->email = fake()->email();
54 | $user->dob = fake()->date();
55 | $user->age = fake()->randomNumber(2, false);
56 | $user->active = fake()->randomNumber(2, false) % 2;
57 | $user->address = fake()->streetAddress();
58 |
59 | return $user->save();
60 | }
61 |
--------------------------------------------------------------------------------
/tests/Unit/Database/DatabaseTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
15 | });
16 | afterAll(function (): void {
17 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
18 | });
19 |
20 | afterEach(function (): void {
21 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
22 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
23 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
24 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS employee CASCADE; ');
25 | });
26 |
27 | it(' checks db()->isUsingUUID() function ', function ($useUUID): void {
28 | if ('UUID' == $useUUID) {
29 | $this->assertTrue(db($useUUID)->isUsingUUID());
30 | } else {
31 | $this->assertFalse(db($useUUID)->isUsingUUID());
32 | }
33 | })->with('useUUID');
34 |
35 | it(' checks db()->create() function ', function ($useUUID): void {
36 | $user = db($useUUID)->create('user');
37 | $this->assertInstanceOf(Scrawler\Arca\Model::class, $user);
38 | })->with('useUUID');
39 |
40 | it('checks if db()->getOne() gets single record', function ($useUUID): void {
41 | populateRandomUser($useUUID);
42 | $id = createRandomUser($useUUID);
43 | $user = db($useUUID)->getOne('user', $id);
44 |
45 | $stmt = db($useUUID)->getConnection()->prepare("SELECT * FROM user WHERE id = '".$id."'");
46 | $result = json_encode($stmt->executeQuery()->fetchAssociative());
47 | $this->assertJsonStringEqualsJsonString(
48 | $result,
49 | $user->toString()
50 | );
51 | $this->assertIsString((string) $user);
52 | $this->assertInstanceOf(Scrawler\Arca\Model::class, $user);
53 | })->with('useUUID');
54 |
55 | it('checks if db()->get() gets all record', function ($useUUID): void {
56 | populateRandomUser($useUUID);
57 | $users = db($useUUID)->get('user');
58 | $stmt = db($useUUID)->getConnection()->prepare('SELECT * FROM user');
59 | $result = json_encode($stmt->executeQuery()->fetchAllAssociative());
60 | $this->assertJsonStringEqualsJsonString(
61 | $result,
62 | $users->toString()
63 | );
64 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, $users);
65 | })->with('useUUID');
66 |
67 | it('checks if db()->find() returns Query Builder', function (): void {
68 | $this->assertInstanceOf(Scrawler\Arca\QueryBuilder::class, db()->find('user'));
69 | $this->assertInstanceOf(Scrawler\Arca\QueryBuilder::class, db()->find('user')->where('id = 2'));
70 | });
71 |
72 | it('checks if db()->find() returns correct records', function (): void {
73 | populateRandomUser();
74 | $users = db()->find('user')->where('active = 1')->get();
75 | $stmt = db()->getConnection()->prepare('SELECT * FROM user WHERE active = 1');
76 | $result = json_encode($stmt->executeQuery()->fetchAllAssociative());
77 | $this->assertJsonStringEqualsJsonString(
78 | $result,
79 | $users->toString()
80 | );
81 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, $users);
82 | });
83 |
84 | it('checks if all public instance of database files are correct', function (): void {
85 | $this->assertInstanceOf(Doctrine\DBAL\Connection::class, db()->getConnection());
86 | });
87 |
88 | it('checks db()->exec() function', function ($useUUID): void {
89 | $user = db($useUUID)->create('user');
90 | $user->name = fake()->name();
91 | $user->save();
92 | if (db($useUUID)->isUsingUUID()) {
93 | db($useUUID)->exec("insert into user (id,name) values ('abc-jfke-dmsk','john')");
94 | $id = 'abc-jfke-dmsk';
95 | } else {
96 | db($useUUID)->exec("insert into user (name) values ('john')");
97 | $id = 2;
98 | }
99 |
100 | $stmt = db($useUUID)->getConnection()->prepare("SELECT * FROM user where id = '".$id."'");
101 | $result = $stmt->executeQuery()->fetchAssociative();
102 | $this->assertEquals($result['name'], 'john');
103 | })->with('useUUID');
104 |
105 | it('checks db()->getAll() function', function ($useUUID): void {
106 | $user = db($useUUID)->create('user');
107 | $user->name = fake()->name();
108 | $user->save();
109 |
110 | $stmt = db($useUUID)->getConnection()->prepare('SELECT * FROM user');
111 | $result = $stmt->executeQuery()->fetchAllAssociative();
112 |
113 | $actual = db($useUUID)->getAll('SELECT * FROM user');
114 |
115 | $this->assertEquals($result, $actual);
116 | })->with('useUUID');
117 |
118 | it('checks db()->delete() function', function ($useUUID): void {
119 | $user = db($useUUID)->create('user');
120 | $user->name = fake()->name();
121 | $id = $user->save();
122 | $user->delete();
123 | $stmt = db($useUUID)->getConnection()->prepare("SELECT * FROM user where id = '".$id."'");
124 | $result = $stmt->executeQuery()->fetchAssociative();
125 | $this->assertEmpty($result);
126 | })->with('useUUID');
127 |
128 | it('checks db()->tableExists() function', function ($useUUID): void {
129 | $user = db($useUUID)->create('user');
130 | $user->name = fake()->name();
131 | $user->save();
132 |
133 | $this->assertTrue(db($useUUID)->tableExists('user'));
134 | })->with('useUUID');
135 |
136 | it('checks db()->tabelsExist() function', function ($useUUID): void {
137 | $user = db($useUUID)->create('user');
138 | $user->name = fake()->name();
139 | $user->save();
140 |
141 | $emp = db($useUUID)->create('employee');
142 | $emp->name = fake()->name();
143 | $emp->save();
144 |
145 | $this->assertTrue(db($useUUID)->tablesExist(['user', 'employee']));
146 | })->with('useUUID');
147 |
148 | it('checks frozen database', function ($useUUID): void {
149 | $user = db($useUUID)->create('user');
150 | $user->name = fake()->name();
151 | $user->save();
152 |
153 | db($useUUID)->freeze();
154 | $user = db($useUUID)->create('user');
155 | $user->name = fake()->name();
156 | $user->email = fake()->email();
157 |
158 | expect(fn () => $user->save())->toThrow(InvalidFieldNameException::class);
159 |
160 | db($useUUID)->unfreeze();
161 | })->with('useUUID');
162 |
163 | it('checks for is UUID', function ($uuid): void {
164 | $val = db($uuid)->isUsingUUID();
165 | $this->assertEquals($val, 'UUID' == $uuid);
166 | })->with('useUUID');
167 |
168 | it('tests registration of json type', function (): void {
169 | $db = db();
170 | $this->assertTrue(Type::hasType('json'));
171 | });
172 |
--------------------------------------------------------------------------------
/tests/Unit/FacadeTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
10 | });
11 | afterAll(function (): void {
12 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
13 | });
14 |
15 | afterEach(function (): void {
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
19 | });
20 |
21 | it('tests DB::connect()', function (): void {
22 | $db = Scrawler\Arca\Facade\Database::connect(getConnectionParams());
23 | $this->assertInstanceOf(Scrawler\Arca\Database::class, $db);
24 | });
25 |
26 | it('tests DB::create()', function (): void {
27 | $user = Scrawler\Arca\Facade\Database::create('user');
28 |
29 | $this->assertInstanceOf(Scrawler\Arca\Model::class, $user);
30 | });
31 |
32 | it('tests DB::get()', function (): void {
33 | populateRandomUser();
34 | $users = Scrawler\Arca\Facade\Database::get('user');
35 | $stmt = db()->getConnection()->prepare('SELECT * FROM user');
36 | $result = json_encode($stmt->executeQuery()->fetchAllAssociative());
37 | $this->assertJsonStringEqualsJsonString(
38 | $result,
39 | $users->toString()
40 | );
41 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, $users);
42 | });
43 |
44 | it('tests DB::getOne()', function (): void {
45 | db();
46 | $id = createRandomUser();
47 | $user = Scrawler\Arca\Facade\Database::getOne('user', $id);
48 |
49 | $stmt = db()->getConnection()->prepare("SELECT * FROM user WHERE id = '".$id."'");
50 | $result = json_encode($stmt->executeQuery()->fetchAssociative());
51 | $this->assertJsonStringEqualsJsonString(
52 | $result,
53 | $user->toString()
54 | );
55 | $this->assertIsString((string) $user);
56 | $this->assertInstanceOf(Scrawler\Arca\Model::class, $user);
57 | });
58 |
59 | it('test DB::exec()', function (): void {
60 | $user = Scrawler\Arca\Facade\Database::create('user');
61 | $user->name = fake()->name();
62 | $user->save();
63 |
64 | Scrawler\Arca\Facade\Database::exec("insert into user (name) values ('john')");
65 | $id = 2;
66 |
67 | $stmt = db()->getConnection()->prepare("SELECT * FROM user where id = '".$id."'");
68 | $result = $stmt->executeQuery()->fetchAssociative();
69 | $this->assertEquals($result['name'], 'john');
70 | });
71 |
72 | it('test DB::delete()', function (): void {
73 | $user = Scrawler\Arca\Facade\Database::create('user');
74 | $user->name = fake()->name();
75 | $id = $user->save();
76 | Scrawler\Arca\Facade\Database::delete($user);
77 | $stmt = db()->getConnection()->prepare("SELECT * FROM user where id = '".$id."'");
78 | $result = $stmt->executeQuery()->fetchAssociative();
79 | $this->assertEmpty($result);
80 | });
81 |
82 | it('test DB::find()', function (): void {
83 | populateRandomUser();
84 | $users = Scrawler\Arca\Facade\Database::find('user')->where('active = 1')->get();
85 | $stmt = db()->getConnection()->prepare('SELECT * FROM user WHERE active = 1');
86 | $result = json_encode($stmt->executeQuery()->fetchAllAssociative());
87 | $this->assertJsonStringEqualsJsonString(
88 | $result,
89 | $users->toString()
90 | );
91 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, $users);
92 | });
93 |
--------------------------------------------------------------------------------
/tests/Unit/Factory/DatabaseFactoryTest.php:
--------------------------------------------------------------------------------
1 | build(getConnectionParams($useUUID));
15 | $this->assertInstanceOf(Scrawler\Arca\Database::class, $db);
16 | $this->assertEquals('UUID' == $useUUID, $db->isUsingUUID());
17 |
18 | $db = $factory->build(getConnectionParams($useUUID, false));
19 | $this->assertInstanceOf(Scrawler\Arca\Database::class, $db);
20 | $this->assertEquals(false, $db->isUsingUUID());
21 | })->with('useUUID');
22 |
23 | it('tests proper initialization of database with container provided', function ($useUUID): void {
24 | $factory = new Scrawler\Arca\Factory\DatabaseFactory(container: new DI\Container());
25 |
26 | $db = $factory->build(getConnectionParams($useUUID));
27 | $this->assertInstanceOf(Scrawler\Arca\Database::class, $db);
28 | $this->assertEquals('UUID' == $useUUID, $db->isUsingUUID());
29 |
30 | $db = $factory->build(getConnectionParams($useUUID, false));
31 | $this->assertInstanceOf(Scrawler\Arca\Database::class, $db);
32 | $this->assertEquals(false, $db->isUsingUUID());
33 | })->with('useUUID');
34 |
--------------------------------------------------------------------------------
/tests/Unit/Manager/ContstraintsTest.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/arca-orm/a6fd66f99ec8a2a15d2842b5bc471d97d7ff671b/tests/Unit/Manager/ContstraintsTest.php
--------------------------------------------------------------------------------
/tests/Unit/Manager/TableManagerTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
10 | });
11 | afterAll(function (): void {
12 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
13 | });
14 |
15 | afterEach(function (): void {
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
17 | });
18 |
19 | it('tests table manger update functionality', function ($useUUID): void {
20 | createRandomUser($useUUID);
21 |
22 | $user = db($useUUID)->create('user');
23 | $user->name = fake()->name();
24 | $user->email = fake()->email();
25 | $user->dob = fake()->date();
26 | $user->age = fake()->randomNumber(2, false);
27 | $user->active = fake()->randomNumber(2, false) % 2;
28 | $user->address = fake()->streetAddress();
29 | $user->rand = 'abc';
30 | $user->randbo = 0 === fake()->randomNumber(2, false) % 2;
31 | $id = $user->save();
32 |
33 | $table = db($useUUID)->getConnection()->createSchemaManager()->introspectTable('user');
34 | $requiredTable = new Doctrine\DBAL\Schema\Table('user');
35 | if (db($useUUID)->isUsingUUID()) {
36 | $requiredTable->addColumn('id', 'string', ['length' => 36, 'notnull' => true, 'comment' => 'string']);
37 | } else {
38 | $requiredTable->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true, 'comment' => 'integer']);
39 | }
40 | $requiredTable->addColumn('name', 'text', ['notnull' => false, 'comment' => 'text']);
41 | $requiredTable->addColumn('email', 'text', ['notnull' => false, 'comment' => 'text']);
42 | $requiredTable->addColumn('dob', 'text', ['notnull' => false, 'comment' => 'text']);
43 | $requiredTable->addColumn('age', 'integer', ['notnull' => false, 'comment' => 'integer']);
44 | $requiredTable->addColumn('active', 'integer', ['notnull' => false, 'comment' => 'integer']);
45 | $requiredTable->addColumn('address', 'text', ['notnull' => false, 'comment' => 'text']);
46 | $requiredTable->addColumn('rand', 'text', ['notnull' => false, 'comment' => 'text']);
47 | $requiredTable->addColumn('randbo', 'boolean', ['notnull' => false, 'comment' => 'boolean']);
48 |
49 | $requiredTable->setPrimaryKey(['id']);
50 |
51 | $actual = new Doctrine\DBAL\Schema\Schema([$table]);
52 | $required = new Doctrine\DBAL\Schema\Schema([$requiredTable]);
53 | $comparator = db()->getConnection()->createSchemaManager()->createComparator();
54 | $diff = $comparator->compareSchemas($actual, $required);
55 | // print_r($diff->toSql(db()->platform));
56 |
57 | $this->assertEmpty(db($useUUID)->getConnection()->getDatabasePlatform()->getAlterSchemaSQL($diff));
58 | })->with('useUUID');
59 |
--------------------------------------------------------------------------------
/tests/Unit/Model/EagerLoadingTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
9 | });
10 | afterAll(function (): void {
11 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
12 | });
13 |
14 | afterEach(function (): void {
15 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user cascade; ');
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent cascade; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user cascade; ');
18 | });
19 |
20 | it('tests for model with() one-to-one relation', function ($useUUID): void {
21 | $user = db($useUUID)->create('user');
22 | $user->name = fake()->name();
23 | $user->email = fake()->email();
24 | $user->dob = fake()->date();
25 | $user->age = fake()->randomNumber(2, false);
26 | $user->address = fake()->streetAddress();
27 | // $user->save();
28 |
29 | $parent = db($useUUID)->create('parent');
30 | $parent->name = fake()->name();
31 | $parent->user = $user;
32 |
33 | $id = $parent->save();
34 |
35 | $parent_retrived_with = db($useUUID)->getOne('parent', $id)->with(['user']);
36 | $parent_retrived = db($useUUID)->getOne('parent', $id);
37 |
38 | $user = db($useUUID)->find('user')->first();
39 |
40 | $this->assertJsonStringNotEqualsJsonString(
41 | $parent_retrived_with->toString(),
42 | $parent_retrived->toString()
43 | );
44 | $parent_retrived->user;
45 | $this->assertJsonStringEqualsJsonString(
46 | $parent_retrived_with->toString(),
47 | $parent_retrived->toString()
48 | );
49 | })->with('useUUID');
50 |
51 | it('tests for model with() one-to-many relation', function ($useUUID): void {
52 | $user1 = db($useUUID)->create('user');
53 | $user1->name = fake()->name();
54 | $user1->email = fake()->email();
55 | $user1->dob = fake()->date();
56 | $user1->age = fake()->randomNumber(2, false);
57 | $user1->address = fake()->streetAddress();
58 |
59 | $user2 = db($useUUID)->create('user');
60 | $user2->name = fake()->name();
61 | $user2->email = fake()->email();
62 | $user2->dob = fake()->date();
63 | $user2->age = fake()->randomNumber(2, false);
64 | $user2->address = fake()->streetAddress();
65 | // $user->save();
66 |
67 | $parent = db($useUUID)->create('parent');
68 | $parent->name = fake()->name();
69 | $parent->ownUserList = [$user1, $user2];
70 |
71 | $id = $parent->save();
72 |
73 | $parent_retrived_with = db($useUUID)->getOne('parent', $id)->with(['ownUserList']);
74 | $parent_retrived = db($useUUID)->getOne('parent', $id);
75 |
76 | $user = db($useUUID)->find('user')->first();
77 |
78 | $this->assertJsonStringNotEqualsJsonString(
79 | $parent_retrived_with->toString(),
80 | $parent_retrived->toString()
81 | );
82 | $parent_retrived->ownUserList;
83 | $this->assertJsonStringEqualsJsonString(
84 | $parent_retrived_with->toString(),
85 | $parent_retrived->toString()
86 | );
87 | })->with('useUUID');
88 |
89 | it('tests for model with() many-to-many relation', function ($useUUID): void {
90 | $user1 = db($useUUID)->create('user');
91 | $user1->name = fake()->name();
92 | $user1->email = fake()->email();
93 | $user1->dob = fake()->date();
94 | $user1->age = fake()->randomNumber(2, false);
95 | $user1->address = fake()->streetAddress();
96 |
97 | $user2 = db($useUUID)->create('user');
98 | $user2->name = fake()->name();
99 | $user2->email = fake()->email();
100 | $user2->dob = fake()->date();
101 | $user2->age = fake()->randomNumber(2, false);
102 | $user2->address = fake()->streetAddress();
103 | // $user->save();
104 |
105 | $parent1 = db($useUUID)->create('parent');
106 | $parent1->name = fake()->name();
107 | $parent1->sharedUserList = [$user1, $user2];
108 |
109 | $parent2 = db($useUUID)->create('parent');
110 | $parent2->name = fake()->name();
111 | $parent2->sharedUserList = [$user1];
112 |
113 | $id1 = $parent1->save();
114 | $id2 = $parent2->save();
115 |
116 | $parent_retrived_with = db($useUUID)->getOne('parent', $id1)->with(['sharedUserList']);
117 | $parent_retrived = db($useUUID)->getOne('parent', $id1);
118 |
119 | $this->assertJsonStringNotEqualsJsonString(
120 | $parent_retrived_with->toString(),
121 | $parent_retrived->toString()
122 | );
123 | $parent_retrived->sharedUserList;
124 | $this->assertJsonStringEqualsJsonString(
125 | $parent_retrived_with->toString(),
126 | $parent_retrived->toString()
127 | );
128 |
129 | $parent_retrived_with = db($useUUID)->getOne('parent', $id2)->with(['sharedUserList']);
130 | $parent_retrived = db($useUUID)->getOne('parent', $id2);
131 |
132 | $this->assertJsonStringNotEqualsJsonString(
133 | $parent_retrived_with->toString(),
134 | $parent_retrived->toString()
135 | );
136 | $parent_retrived->sharedUserList;
137 | $this->assertJsonStringEqualsJsonString(
138 | $parent_retrived_with->toString(),
139 | $parent_retrived->toString()
140 | );
141 | })->with('useUUID');
142 |
--------------------------------------------------------------------------------
/tests/Unit/Model/ExceptionTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
14 | });
15 | afterAll(function (): void {
16 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
17 | });
18 |
19 | afterEach(function (): void {
20 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
21 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
22 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
23 | });
24 |
25 | it('check exception is thrown when non existent key is accessed', function ($useUUID): void {
26 | $id = createRandomUser($useUUID);
27 | $user = db($useUUID)->getOne('user', $id);
28 | $user->somekey;
29 | })->throws(Scrawler\Arca\Exception\KeyNotFoundException::class, 'Key you are trying to access does not exist')->with('useUUID');
30 |
31 | it('checks exception is thrown when id is force set on a model', function ($useUUID): void {
32 | $user = db($useUUID)->create('user');
33 | $user->id = 1;
34 | $user->name = fake()->name();
35 | $user->email = fake()->email();
36 | $user->save();
37 | })->with('useUUID')->throws(Scrawler\Arca\Exception\InvalidIdException::class, 'Force setting of id for model is not allowed');
38 |
39 | it('checks exception is thrown if share list is not array of model', function ($useUUID): void {
40 | $parent = db($useUUID)->create('parent');
41 | $parent->name = fake()->name();
42 | $parent->sharedUserList = ['test', 'test1'];
43 | $id = $parent->save();
44 | })->with('useUUID')->throws(Scrawler\Arca\Exception\InvalidModelException::class);
45 |
46 | it('checks exception is thrown if own list is not array of model', function ($useUUID): void {
47 | $parent = db($useUUID)->create('parent');
48 | $parent->name = fake()->name();
49 | $parent->ownUserList = ['test', 'test1'];
50 | $id = $parent->save();
51 | })->with('useUUID')->throws(Scrawler\Arca\Exception\InvalidModelException::class, 'parameter passed to shared list or own list should be array of class \Arca\Model');
52 |
--------------------------------------------------------------------------------
/tests/Unit/Model/ModelTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
11 | });
12 | afterAll(function (): void {
13 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
14 | });
15 |
16 | afterEach(function (): void {
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
19 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
20 | });
21 |
22 | it('checks if model is properly populated on retrive', function ($useUUID): void {
23 | $id = createRandomUser($useUUID);
24 | $stmt = db($useUUID)->getConnection()->prepare("SELECT * FROM user WHERE id ='".$id."'");
25 | $user = db($useUUID)->getOne('user', $id);
26 | $result = $stmt->executeQuery()->fetchAssociative();
27 | $this->assertEquals($user->name, $result['name']);
28 | $this->assertEquals($user->getProperties(), $result);
29 | })->with('useUUID');
30 |
31 | it('tests for model equals', function ($useUUID): void {
32 | $id = createRandomUser($useUUID);
33 | $user = db($useUUID)->getOne('user', $id);
34 | $user_two = db($useUUID)->getOne('user', $id);
35 | $this->assertTrue($user->equals($user_two));
36 | })->with('useUUID');
37 |
38 | it('tests for model not equals', function ($useUUID): void {
39 | $id = createRandomUser($useUUID);
40 | $user = db($useUUID)->getOne('user', $id);
41 | $user_two = db($useUUID)->getOne('user', $id);
42 | $user_two->name = 'test';
43 | $this->assertFalse($user->equals($user_two));
44 | })->with('useUUID');
45 |
46 | it('checks isset() function of model', function ($useUUID): void {
47 | $id = createRandomUser($useUUID);
48 | $user = db($useUUID)->getOne('user', $id);
49 | $truey = isset($user->name);
50 | $falsey = isset($user->somekey);
51 | $this->assertTrue($truey);
52 | $this->assertFalse($falsey);
53 | })->with('useUUID');
54 |
55 | it('tests bulk property setting', function ($useUUID): void {
56 | $user = db($useUUID)->create('user');
57 | $user->setProperties([
58 | 'name' => fake()->name(),
59 | 'email' => fake()->email(),
60 | 'dob' => fake()->date(),
61 | 'age' => fake()->randomNumber(2, false),
62 | 'address' => fake()->streetAddress(),
63 | ]);
64 | $id = $user->save();
65 | $user_retrived = db($useUUID)->getOne('user', $id);
66 | $this->assertEquals($user->name, $user_retrived->name);
67 | $this->assertEquals($user->email, $user_retrived->email);
68 | })->with('useUUID');
69 |
70 | it('tests for model delete() function', function ($useUUID): void {
71 | $user = db($useUUID)->create('user');
72 | $user->name = fake()->name();
73 | $user->email = fake()->email();
74 | $id = $user->save();
75 | $user->delete();
76 | $this->assertNull(db()->getOne('user', $id));
77 | })->with('useUUID');
78 |
79 | it('tests for model clean on load function', function ($useUUID): void {
80 | $user = db($useUUID)->create('user');
81 | $user->name = fake()->name();
82 | $user->email = fake()->email();
83 | $id = $user->save();
84 |
85 | $user = db($useUUID)->getOne('user', $id);
86 | $this->assertFalse($user->hasIdError());
87 | $this->assertFalse($user->hasForeign('oto'));
88 | $this->assertFalse($user->hasForeign('otm'));
89 | $this->assertFalse($user->hasForeign('mtm'));
90 | })->with('useUUID');
91 |
92 | it('tests id is always 0 before save on a new model', function ($useUUID): void {
93 | $user = db($useUUID)->create('user');
94 | $this->assertEquals($user->getId(), 0);
95 | $user->name = fake()->name();
96 | $user->email = fake()->email();
97 | $id = $user->save();
98 | $this->assertNotEquals($user->getId(), 0);
99 | $this->assertEquals($user->getId(), $id);
100 | })->with('useUUID');
101 |
102 | it('tests model refresh', function ($useUUID): void {
103 | $user1 = db($useUUID)->create('user');
104 | $user1->name = fake()->name();
105 | $user1->email = fake()->email();
106 |
107 | $parent = db($useUUID)->create('parent');
108 | $parent->name = fake()->name();
109 | $parent->ownUserList = [$user1];
110 | $id = $parent->save();
111 |
112 | $parent = db($useUUID)->getOne('parent', $id);
113 | $this->assertEquals($parent->ownUserList->first()->name, $user1->name);
114 | $user = $parent->ownUserList->first();
115 | $user->name = 'test';
116 | $user->save();
117 | $this->assertNotEquals($parent->ownUserList->first()->name, 'test');
118 | $parent->refresh();
119 | $this->assertEquals($parent->ownUserList->first()->name, 'test');
120 |
121 |
122 |
123 | })->with('useUUID');
124 |
--------------------------------------------------------------------------------
/tests/Unit/Model/PropertyTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
13 | });
14 | afterAll(function (): void {
15 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
16 | });
17 |
18 | afterEach(function (): void {
19 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS grandparent CASCADE; ');
20 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
21 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
22 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
23 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS child CASCADE; ');
24 | });
25 |
26 | it('tests model properties with multiple realtions', function ($useUUID): void {
27 | $child1 = db($useUUID)->create('child');
28 | $child1->name = fake()->name();
29 | $child1->email = fake()->email();
30 | $child1->dob = fake()->date();
31 | $child1->age = fake()->randomNumber(2, false);
32 |
33 | $child2 = db($useUUID)->create('child');
34 | $child2->name = fake()->name();
35 | $child2->email = fake()->email();
36 | $child2->dob = fake()->date();
37 | $child2->age = fake()->randomNumber(2, false);
38 |
39 | $child3 = db($useUUID)->create('user');
40 | $child3->name = fake()->name();
41 | $child3->email = fake()->email();
42 | $child3->dob = fake()->date();
43 | $child3->age = fake()->randomNumber(2, false);
44 |
45 | $grandfater = db($useUUID)->create('grandparent');
46 | $grandfater->name = fake()->name();
47 | $grandfater->email = fake()->email();
48 | $grandfater->dob = fake()->date();
49 | $grandfater->age = fake()->randomNumber(2, false);
50 |
51 | $parent = db($useUUID)->create('parent');
52 | $parent->name = fake()->name();
53 | $parent->grandparent = $grandfater;
54 | $parent->ownChildList = [$child1, $child2];
55 | $parent->sharedUserList = [$child3];
56 | $id = $parent->save();
57 |
58 | $this->assertTrue($child1->isLoaded());
59 | $this->assertTrue($child2->isLoaded());
60 | $this->assertTrue($child3->isLoaded());
61 | $this->assertTrue($grandfater->isLoaded());
62 |
63 | $parent_retrived = db($useUUID)->getOne('parent', $id);
64 | $this->assertEquals($parent->name, $parent_retrived->name);
65 | $this->assertTrue(isset($parent_retrived->grandparent_id));
66 | $this->assertFalse(isset($parent_retrived->grandparent));
67 | $this->assertEquals($grandfater->name, $parent_retrived->grandparent->name);
68 | $this->assertFalse(isset($parent_retrived->grandparent_id));
69 | $this->assertTrue(isset($parent_retrived->grandparent));
70 | $this->assertEquals(count($parent_retrived->ownChildList), 2);
71 |
72 | $this->assertTrue($parent_retrived->ownChildList->first()->name == $child1->name || $parent_retrived->ownChildList->first()->name == $child2->name);
73 | $this->assertEquals(count($parent_retrived->sharedUserList), 1);
74 | $this->assertEquals($parent_retrived->sharedUserList->first()->name, $child3->name);
75 | })->with('useUUID');
76 |
--------------------------------------------------------------------------------
/tests/Unit/Model/RelationsTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
10 | });
11 | AfterAll(function (): void {
12 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
13 | });
14 |
15 | afterEach(function (): void {
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
19 | });
20 |
21 | it('checks if model can retrive one-to-one related models', function ($useUUID): void {
22 | $user = db($useUUID)->create('user');
23 | $user->name = fake()->name();
24 | $user->email = fake()->email();
25 | $user->dob = fake()->date();
26 | $user->age = fake()->randomNumber(2, false);
27 | $user->address = fake()->streetAddress();
28 | // $id = $user->save();
29 |
30 | $parent = db($useUUID)->create('parent');
31 | $parent->name = fake()->name();
32 | $parent->user = $user;
33 | $id = $parent->save();
34 |
35 | $user_retrived = $parent->user;
36 | if (!db($useUUID)->isUsingUUID()) {
37 | unset($user_retrived->id);
38 | }
39 | $this->assertJsonStringEqualsJsonString(
40 | $user_retrived->toString(),
41 | $user->toString()
42 | );
43 | })->with('useUUID');
44 |
45 | it('checks if model can retrive one-to-many related models', function ($useUUID): void {
46 | $user = db($useUUID)->create('user');
47 | $user->name = fake()->name();
48 | $user->email = fake()->email();
49 | $user->dob = fake()->date();
50 | $user->age = fake()->randomNumber(2, false);
51 | $user->address = fake()->streetAddress();
52 |
53 | $user_two = db($useUUID)->create('user');
54 | $user_two->name = fake()->name();
55 | $user_two->email = fake()->email();
56 | $user_two->dob = fake()->date();
57 | $user_two->age = fake()->randomNumber(2, false);
58 | $user_two->address = fake()->streetAddress();
59 | // $id = $user->save();
60 |
61 | $parent = db($useUUID)->create('parent');
62 | $parent->name = fake()->name();
63 | $parent->ownUserList = [$user, $user_two];
64 | $id = $parent->save();
65 |
66 | $parent_retrived = db($useUUID)->getOne('parent', $id);
67 | $users_retrived = $parent->ownUserList->apply(function ($user): void {
68 | unset($user->id);
69 | });
70 | if (db($useUUID)->isUsingUUID()) {
71 | unset($user->id);
72 | unset($user_two->id);
73 | }
74 |
75 | $test_collection = Scrawler\Arca\Collection::fromIterable([$user, $user_two])
76 | ->map(static fn ($model): \Scrawler\Arca\Model => $model->setLoaded());
77 | $test_collection_two = Scrawler\Arca\Collection::fromIterable([$user_two, $user])
78 | ->map(static fn ($model): \Scrawler\Arca\Model => $model->setLoaded());
79 |
80 | $this->assertTrue(
81 | $users_retrived->toString() == $test_collection->toString() || $users_retrived->toString() == $test_collection_two->toString()
82 | );
83 | })->with('useUUID');
84 |
85 | it('checks if model can retrive many-to-many related models', function ($useUUID): void {
86 | $user = db($useUUID)->create('user');
87 | $user->name = fake()->name();
88 | $user->email = fake()->email();
89 | $user->dob = fake()->date();
90 | $user->age = fake()->randomNumber(2, false);
91 | $user->address = fake()->streetAddress();
92 |
93 | $user_two = db($useUUID)->create('user');
94 | $user_two->name = fake()->name();
95 | $user_two->email = fake()->email();
96 | $user_two->dob = fake()->date();
97 | $user_two->age = fake()->randomNumber(2, false);
98 | $user_two->address = fake()->streetAddress();
99 | // $id = $user->save();
100 |
101 | $parent = db($useUUID)->create('parent');
102 | $parent->name = fake()->name();
103 | $parent->sharedUserList = [$user, $user_two];
104 | $id = $parent->save();
105 |
106 | $parent_retrived = db()->getOne('parent', $id);
107 | $users_retrived = $parent->sharedUserList->apply(function ($user): void {
108 | unset($user->id);
109 | });
110 |
111 | if (db($useUUID)->isUsingUUID()) {
112 | unset($user->id);
113 | unset($user_two->id);
114 | }
115 |
116 | $test_collection = Scrawler\Arca\Collection::fromIterable([$user, $user_two])
117 | ->map(static fn ($model): \Scrawler\Arca\Model => $model->setLoaded());
118 |
119 | $test_collection_two = Scrawler\Arca\Collection::fromIterable([$user_two, $user])
120 | ->map(static fn ($model): \Scrawler\Arca\Model => $model->setLoaded());
121 | $this->assertTrue(
122 | $users_retrived->toString() == $test_collection->toString() || $users_retrived->toString() == $test_collection_two->toString()
123 | );
124 | })->with('useUUID');
125 |
--------------------------------------------------------------------------------
/tests/Unit/Model/TraitsTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
9 | });
10 | afterAll(function (): void {
11 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
12 | });
13 |
14 | afterEach(function (): void {
15 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
18 | });
19 |
20 | it('tests model can be treated as iterable', function (): void {
21 | $model = db()->create('user');
22 | $model->name = fake()->name();
23 | $model->email = fake()->email();
24 | $model->dob = fake()->date();
25 | $model->age = fake()->randomNumber(2, false);
26 | $model->address = fake()->streetAddress();
27 | $model->save();
28 |
29 | $model = db()->find('user')->first();
30 | foreach ($model as $key => $value) {
31 | $this->assertNotNull($key);
32 | $this->assertNotNull($value);
33 | }
34 | });
35 |
36 | it('tests model can be treated as Array', function (): void {
37 | $model = db()->create('user');
38 | $model->name = fake()->name();
39 | $model->email = fake()->email();
40 | $model->dob = fake()->date();
41 | $model->age = fake()->randomNumber(2, false);
42 | $model->address = fake()->streetAddress();
43 | $model->save();
44 |
45 | $model = db()->find('user')->first();
46 | $this->assertIsArray($model->toArray());
47 | $this->assertEquals($model['name'], $model->name);
48 | $this->assertTrue(isset($model['email']));
49 | unset($model['age']);
50 | $this->assertFalse($model->isset('age'));
51 |
52 | $model['age'] = 10;
53 | $this->assertEquals($model['age'], $model->age);
54 |
55 | expect(fn (): int => $model[] = 10)->toThrow(Exception::class);
56 | });
57 |
58 | it('tests if class is stringable', function (): void {
59 | $model = db()->create('user');
60 | $model->name = fake()->name();
61 | $model->email = fake()->email();
62 | $model->save();
63 |
64 | ob_start();
65 | echo $model;
66 | $data = ob_get_clean();
67 |
68 | $this->assertEquals($data, $model->toString());
69 | });
70 |
--------------------------------------------------------------------------------
/tests/Unit/Model/TypeTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
11 | });
12 | afterAll(function (): void {
13 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
14 | });
15 |
16 | afterEach(function (): void {
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
19 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
20 | });
21 |
22 | it('tests for types when loaded ', function ($useUUID): void {
23 | $id = createRandomUser($useUUID);
24 | $user_retrived = db($useUUID)->getOne('user', $id);
25 | $types = $user_retrived->getTypes();
26 | $this->assertIsArray($types);
27 | $this->assertEquals($types['name'], 'text');
28 | $this->assertEquals($types['email'], 'text');
29 | })->with('useUUID');
30 |
31 | it('tests boolean type bug', function ($useUUID): void {
32 | $user = db()->create('user');
33 | $user->name = fake()->name();
34 | $user->email = fake()->email();
35 | $user->active = true;
36 | $id = $user->save();
37 | $user_retrived = db()->getOne('user', $id);
38 | $this->assertTrue($user_retrived->active);
39 |
40 | $user->name = fake()->name();
41 | $user->email = fake()->email();
42 | $user->active = false;
43 | $id = $user->save();
44 | $user_retrived = db()->getOne('user', $id);
45 | $this->assertFalse($user_retrived->active);
46 | })->with('useUUID');
47 |
48 | it('checks for storing array in db', function ($useUUID): void {
49 | $user = db($useUUID)->create('user');
50 | $user->name = fake()->name();
51 | $user->email = fake()->email();
52 | $user->hobbies = ['swimming', 'cycling', 'running'];
53 | $id = $user->save();
54 |
55 | $user_retrived = db($useUUID)->getOne('user', $id);
56 | $hobby = $user_retrived->hobbies;
57 | expect($hobby)->toBeArray();
58 | })->with('useUUID');
59 |
--------------------------------------------------------------------------------
/tests/Unit/QueryBuilder/EagerLoadingTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
10 | });
11 | afterAll(function (): void {
12 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
13 | });
14 |
15 | afterEach(function (): void {
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS grandparent CASCADE; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
19 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
20 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS child CASCADE; ');
21 | });
22 |
23 | it('checks if db()->find()->with() eager loads relation', function ($useUUID): void {
24 | $user = db($useUUID)->create('user');
25 | $user->name = fake()->name();
26 | $user->email = fake()->email();
27 | $user->dob = fake()->date();
28 | $user->age = fake()->randomNumber(2, false);
29 | $user->address = fake()->streetAddress();
30 | // $user->save();
31 |
32 | $parent = db($useUUID)->create('parent');
33 | $parent->name = fake()->name();
34 | $parent->user = $user;
35 | $parent->save();
36 |
37 | $parent_retrived = db($useUUID)->find('parent')->with('user')->first();
38 | $user = db($useUUID)->find('user')->first();
39 |
40 | $this->assertJsonStringEqualsJsonString(
41 | $parent_retrived->user->toString(),
42 | $user->toString()
43 | );
44 | })->with('useUUID');
45 |
46 | it('checks if db()->find()->with() eager loads multiple realtions', function ($useUUID): void {
47 | $child1 = db($useUUID)->create('child');
48 | $child1->name = fake()->name();
49 | $child1->email = fake()->email();
50 | $child1->dob = fake()->date();
51 | $child1->age = fake()->randomNumber(2, false);
52 |
53 | $child2 = db($useUUID)->create('child');
54 | $child2->name = fake()->name();
55 | $child2->email = fake()->email();
56 | $child2->dob = fake()->date();
57 | $child2->age = fake()->randomNumber(2, false);
58 |
59 | $child3 = db($useUUID)->create('user');
60 | $child3->name = fake()->name();
61 | $child3->email = fake()->email();
62 | $child3->dob = fake()->date();
63 | $child3->age = fake()->randomNumber(2, false);
64 |
65 | $grandfater = db($useUUID)->create('grandparent');
66 | $grandfater->name = fake()->name();
67 | $grandfater->email = fake()->email();
68 | $grandfater->dob = fake()->date();
69 | $grandfater->age = fake()->randomNumber(2, false);
70 |
71 | $parent = db($useUUID)->create('parent');
72 | $parent->name = fake()->name();
73 | $parent->grandparent = $grandfater;
74 | $parent->ownChildList = [$child1, $child2];
75 | $parent->sharedUserList = [$child3];
76 | $id = $parent->save();
77 |
78 | $parent_retrived = db($useUUID)->find('parent')->where('id = ?')->setParameter(0, $id)->with('ownChildList')->with('sharedUserList')->with('grandparent')->first();
79 | $parent_simple = db($useUUID)->getOne('parent', $id);
80 |
81 | $this->assertJsonStringNotEqualsJsonString(
82 | $parent_retrived->toString(),
83 | $parent_simple->toString()
84 | );
85 |
86 | $parent_simple->ownChildList;
87 | $parent_simple->sharedUserList;
88 | $parent_simple->grandparent;
89 |
90 | $this->assertJsonStringEqualsJsonString(
91 | $parent_retrived->toString(),
92 | $parent_simple->toString()
93 | );
94 | })->with('useUUID');
95 |
--------------------------------------------------------------------------------
/tests/Unit/QueryBuilder/QueryBuilderTest.php:
--------------------------------------------------------------------------------
1 | getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
10 | });
11 | afterAll(function (): void {
12 | db()->getConnection()->executeStatement('SET FOREIGN_KEY_CHECKS=1;');
13 | });
14 |
15 | afterEach(function (): void {
16 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent_user CASCADE; ');
17 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS user CASCADE; ');
18 | db()->getConnection()->executeStatement('DROP TABLE IF EXISTS parent CASCADE; ');
19 | });
20 |
21 | it('checks if db()->find()->first() returns first record', function ($useUUID): void {
22 | $id = createRandomUser($useUUID);
23 | $user = db($useUUID)->find('user')->first();
24 | $stmt = db($useUUID)->getConnection()->prepare("SELECT * FROM user WHERE id = '".$id."'");
25 | $result = json_encode($stmt->executeQuery()->fetchAssociative());
26 | $this->assertJsonStringEqualsJsonString(
27 | $result,
28 | $user->toString()
29 | );
30 | $this->assertInstanceOf(Scrawler\Arca\Model::class, $user);
31 | })->with('useUUID');
32 |
33 | it('checks if null is returned if table does not exist', function (): void {
34 | $this->assertNull(db()->find('non_existent_table')->first());
35 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, db()->find('non_existent_table')->get());
36 | $this->assertEmpty(db()->find('non_existent_table')->get()->toArray());
37 | });
38 |
39 | it('checks if null is returned if table empty', function (): void {
40 | $user = db()->create('user');
41 | $user->name = fake()->name();
42 | $user->email = fake()->email();
43 | $user->save();
44 | $user->delete();
45 |
46 | $this->assertNull(db()->find('user')->first());
47 | $this->assertInstanceOf(Scrawler\Arca\Collection::class, db()->find('user')->get());
48 | $this->assertEmpty(db()->find('user')->get()->toArray());
49 | });
50 |
--------------------------------------------------------------------------------