├── .github ├── logo.jpg └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── composer.json ├── phpstan.neon ├── phpunit.xml ├── src ├── Model │ ├── Builder │ │ └── OrderBuilder.php │ ├── Customer.php │ ├── Item.php │ ├── Meta │ │ └── ItemMeta.php │ ├── Order.php │ ├── Product.php │ ├── ProductAttribute.php │ ├── ProductCategory.php │ ├── ProductTag.php │ └── ProductType.php ├── Support │ ├── Address.php │ ├── BillingAddress.php │ ├── Payment.php │ └── ShippingAddress.php ├── Traits │ ├── AddressesTrait.php │ └── HasRelationsThroughMeta.php └── WooCommerce.php ├── stubs └── Corcel.stub └── tests ├── TestCase.php ├── Unit ├── Model │ ├── CustomerTest.php │ ├── ItemTest.php │ ├── OrderTest.php │ ├── ProductAttributeTest.php │ └── ProductTest.php ├── Support │ ├── AddressTest.php │ ├── BillingAddressTest.php │ ├── PaymentTest.php │ └── ShippingAddressTest.php ├── Traits │ └── HasRelationsThroughMetaTest.php └── WooCommerceTest.php └── database ├── factories ├── CustomerFactory.php ├── ItemFactory.php ├── OrderFactory.php └── ProductFactory.php └── migrations ├── 2020_01_01_000000_create_options_table.php ├── 2020_01_01_000000_create_postmeta_table.php ├── 2020_01_01_000000_create_posts_table.php ├── 2020_01_01_000000_create_term_relationships_table.php ├── 2020_01_01_000000_create_term_taxonomy_table.php ├── 2020_01_01_000000_create_terms_table.php ├── 2020_01_01_000000_create_usermeta_table.php ├── 2020_01_01_000000_create_users_table.php ├── 2020_01_01_000000_create_woocommerce_attribute_taxonomies_table.php ├── 2020_01_01_000000_create_woocommerce_order_itemmeta_table.php └── 2020_01_01_000000_create_woocommerce_order_items_table.php /.github/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corcel/woocommerce/d2921dc3f2d32b751628b8a889be96f2be506fdf/.github/logo.jpg -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on every push 6 | on: push 7 | 8 | jobs: 9 | run: 10 | 11 | strategy: 12 | matrix: 13 | php-version: 14 | - "8.2" 15 | - "8.3" 16 | - "8.4" 17 | operating-system: 18 | - ubuntu-latest 19 | - windows-latest 20 | 21 | runs-on: ${{ matrix.operating-system }} 22 | 23 | name: Analyse and test code (PHP ${{ matrix.php-version }}, ${{ matrix.operating-system }}) 24 | 25 | steps: 26 | 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Setup PHP 31 | uses: shivammathur/setup-php@v2 32 | with: 33 | php-version: "${{ matrix.php-version }}" 34 | extensions: sqlite, pdo_sqlite, fileinfo 35 | 36 | - name: Get composer cache directory 37 | id: composercache 38 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 39 | 40 | - name: Cache dependencies 41 | uses: actions/cache@v4 42 | with: 43 | path: ${{ steps.composercache.outputs.dir }} 44 | key: php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} 45 | restore-keys: php-${{ matrix.php-version }}-composer- 46 | 47 | - name: Install dependencies 48 | run: composer update --no-interaction --no-progress 49 | 50 | - name: PHPStan 51 | run: vendor/bin/phpstan analyse 52 | 53 | - name: PHPUnit 54 | run: vendor/bin/phpunit 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | .phpunit.result.cache -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # WooCommerce Corcel Plugin changelog 2 | 3 | ## v6.0.0 - 2025-04-29 4 | 5 | * Laravel 12 support (#43) 6 | * Add support for Corcel v9 7 | 8 | ## v5.0.1 - 2024-06-16 9 | 10 | * Add support for Corcel v8 (#41) 11 | 12 | ## v5.0.0 - 2024-05-15 13 | 14 | * Laravel 11 support (#37, #40) 15 | * Add flexibility to inherit the models and define and update the model factories (#38, #39) 16 | 17 | ## v4.0.0 - 2023-06-12 18 | 19 | * Laravel 10 support (#35) 20 | 21 | ## v3.0.0 - 2022-06-25 22 | 23 | * Laravel 9 support (#29) 24 | 25 | ## v2.1.1 - 2022-06-25 26 | 27 | * Fix WooCommerce::currency() method return type (#30) 28 | 29 | ## v2.1.0 - 2021-10-21 30 | 31 | * PHP 8.0 support (c95576e) 32 | 33 | ## v2.0.3 - 2020-12-10 34 | 35 | * Fix: allow address to work with subclasses (#20) 36 | 37 | ## v2.0.2 - 2020-10-30 38 | 39 | * Fix error with parsing order completed and paid date from timestamp (#19) 40 | 41 | ## v2.0.1 - 2020-10-29 42 | 43 | * Fix invalid typehint in ProductAttribute model (#17) 44 | 45 | ## v2.0.0 - 2020-10-10 46 | 47 | * Add support for Corcel 5.x and Laravel 8.x 48 | * Drop support for PHP 7.2 49 | 50 | ## v1.0.1 - 2020-10-04 51 | 52 | * PHP 7.2 compatibility (f4d8265) 53 | 54 | ## v1.0.0 - 2020-10-04 55 | 56 | * Inital release -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at krzysztof@grabania.pl. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2020 Grabania.pl Krzysztof Grabania 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 |

3 | Corcel WooCommerce logo 4 |

5 | 6 | **A collection of Laravel models for WooCommerce.** 7 | 8 | This plugin extends base [Corcel](https://github.com/corcel/corcel) package and allows fetching WooCommerce data from WordPress database. Currently this plugin implements the following models: 9 | 10 | * [Customer](#customer-model) - wrapper for User model 11 | * [Order](#order-model) - wrapper for Post model 12 | * [Product](#product-model) - wrapper for Post model 13 | * [Item](#item-model) 14 | * Product Category - wrapper for Taxonomy model 15 | * Product Tag - wrapper for Taxonomy model 16 | * Product Attribute - helper model for getting product attributes (should not be used directly) 17 | * Product Type - helper model for getting product types (should not be used directly) 18 | 19 | Some meta values are collected into helper classes: 20 | 21 | * [BillingAddress](#billingaddress-helper) - helper for customer and order billing address 22 | * [ShippingAddress](#shippingaddress-helper) - helper for customer and order shipping address 23 | * [Payment](#payment-helper) - helper for order payment 24 | 25 | ## Compatibility list 26 | 27 | | Corcel WooCommerce | Laravel | PHP version | Supported | 28 | | ----------------------------------------------------- | -------------- | ------------- | ------------------ | 29 | | 6.x (master) | 11.x, 12.x | >= 8.2 | :white_check_mark: | 30 | | [5.x](https://github.com/corcel/woocommerce/tree/5.x) | 10.x, 11.x | >= 8.2 | :white_check_mark: | 31 | | [4.x](https://github.com/corcel/woocommerce/tree/4.x) | 10.x | >= 8.1 | :x: | 32 | | [3.x](https://github.com/corcel/woocommerce/tree/3.x) | 9.x | >= 8.0.3 | :x: | 33 | | [2.x](https://github.com/corcel/woocommerce/tree/2.x) | 6.x, 7.x, 8.x | >= 7.3 \| 8.0 | :x: | 34 | | [1.x](https://github.com/corcel/woocommerce/tree/1.x) | 6.x, 7.x | >= 7.2 | :x: | 35 | 36 | ## Installation 37 | 38 | ```bash 39 | composer require corcel/woocommerce 40 | ``` 41 | 42 | ## Models list 43 | 44 | ### Customer model 45 | 46 | #### Get customer by id 47 | 48 | ```php 49 | $customer = Customer::find(1); 50 | ``` 51 | 52 | #### Customer relation 53 | 54 | ```php 55 | $customer = Customer::find(1); 56 | 57 | $customerOrders = $customer->orders; 58 | ``` 59 | 60 | #### Customer properties 61 | 62 | ```php 63 | $customer = Customer::find(1); 64 | 65 | $customer->order_count; // e.g. 10 66 | $customer->billing_address; // \Corcel\WooCommerce\Support\BillingAddress class instance 67 | $customer->shipping_address; // \Corcel\WooCommerce\Support\ShippingAddress class instance 68 | ``` 69 | 70 | ### Order model 71 | 72 | #### Get order by id 73 | 74 | ```php 75 | $order = Order::find(1); 76 | ``` 77 | 78 | #### Get completed orders 79 | 80 | ```php 81 | $completedOrders = Order::completed()->get(); 82 | ``` 83 | 84 | *For other statuses methods please check [OrderBuilder.php](src/Model/Builder/OrderBuilder.php).* 85 | 86 | #### Order relations 87 | 88 | ```php 89 | $order = Order::find(1); 90 | 91 | $orderItems = $order->items; 92 | $orderCustomer = $order->customer; 93 | ``` 94 | 95 | #### Order properties 96 | 97 | ```php 98 | $order = Order::find(1); 99 | 100 | $order->currency; // e.g. EUR 101 | $order->total; // e.g. 10.20 102 | $order->tax; // e.g. 0.50 103 | $order->shipping_tax; // e.g. 0.20 104 | $order->status; // e.g. completed 105 | $order->date_completed; // e.g. 2020-06-01 10:00:00 106 | $order->date_paid; // e.g. 2020-06-01 10:00:00 107 | $order->payment; // \Corcel\WooCommerce\Support\Payment class instance 108 | $order->billing_address; // \Corcel\WooCommerce\Support\BillingAddress class instance 109 | $order->shipping_address; // \Corcel\WooCommerce\Support\ShippingAddress class instance 110 | ``` 111 | 112 | ### Item model 113 | 114 | #### Get item by id 115 | 116 | ```php 117 | $item = Item::find(1); 118 | ``` 119 | 120 | #### Item relations 121 | 122 | ```php 123 | $item = Item::find(1); 124 | 125 | $itemOrder = $item->order; 126 | $itemProduct = $item->product; 127 | ``` 128 | 129 | #### Item properties 130 | 131 | ```php 132 | $item = Item::find(1); 133 | 134 | $item->order_item_id; 135 | $item->order_item_name; 136 | $item->order_item_type; 137 | $item->order_id; 138 | $item->product_id; 139 | $item->variation_id; 140 | $item->quantity; // e.g. 2 141 | $item->tax_class; 142 | $item->line_subtotal; // e.g. 5.50 143 | $item->line_subtotal_tax; // e.g. 0.50 144 | $item->line_total; // e.g. 10.50 145 | $item->line_tax; // e.g. 2.00 146 | ``` 147 | 148 | ### Product model 149 | 150 | #### Get product by id 151 | 152 | ```php 153 | $product = Product::find(1); 154 | ``` 155 | 156 | #### Product relations 157 | 158 | ```php 159 | $product = Product::find(1); 160 | 161 | $product->categories; 162 | $product->items; 163 | $product->tags; 164 | ``` 165 | 166 | #### Product properties 167 | 168 | ```php 169 | $product = Product::find(1); 170 | 171 | $product->price; 172 | $product->regular_price; 173 | $product->sale_price; 174 | $product->on_sale; 175 | $product->sku; 176 | $product->tax_status; 177 | $product->is_taxable; 178 | $product->weight; 179 | $product->length; 180 | $product->width; 181 | $product->height; 182 | $product->is_virtual; 183 | $product->is_downloadable; 184 | $product->stock; 185 | $product->in_stock; 186 | $product->type; 187 | $product->attributes; // Collection of (variation) attributes 188 | $product->crosssells; // Collection of cross-sell products 189 | $product->upsells; // Collection of up-sell products 190 | $product->gallery; // Collection of gallery attachments 191 | ``` 192 | 193 | ## Helper classes list 194 | 195 | ### BillingAddress helper 196 | 197 | This class collects customer and order meta related to billing address. 198 | 199 | ```php 200 | $order = Order::find(1); 201 | $address = $order->billingAddress; 202 | 203 | $address->first_name; 204 | $address->last_name; 205 | $address->company; 206 | $address->address_1; 207 | $address->address_2; 208 | $address->city; 209 | $address->state; 210 | $address->postcode; 211 | $address->country; 212 | $address->email; 213 | $address->phone; 214 | ``` 215 | 216 | ### ShippingAddress helper 217 | 218 | This class collects customer and order meta related to billing address. 219 | 220 | ```php 221 | $order = Order::find(1); 222 | $address = $order->shippingAddress; 223 | 224 | $address->first_name; 225 | $address->last_name; 226 | $address->company; 227 | $address->address_1; 228 | $address->address_2; 229 | $address->city; 230 | $address->state; 231 | $address->postcode; 232 | $address->country; 233 | ``` 234 | 235 | ### Payment helper 236 | 237 | This class collects order meta related to payment. 238 | 239 | ```php 240 | $order = Order::find(1); 241 | $payment = $order->payment; 242 | 243 | $payment->method; 244 | $payment->method_title; 245 | $payment->transaction_id; 246 | ``` 247 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corcel/woocommerce", 3 | "description": "WooCommerce plugin for Corcel", 4 | "homepage": "http://github.com/corcel/woocommerce", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Krzysztof Grabania", 9 | "email": "krzysztof@grabania.pl", 10 | "homepage": "https://grabania.pl" 11 | } 12 | ], 13 | "require": { 14 | "php": "^8.2", 15 | "illuminate/database": "^11.0|^12.0", 16 | "illuminate/support": "^11.0|^12.0", 17 | "jgrossi/corcel": "^8.0|^9.0", 18 | "nesbot/carbon": "^2.66|^3.0" 19 | }, 20 | "require-dev": { 21 | "orchestra/testbench": "^8.0|^9.0|^10.0", 22 | "phpstan/phpstan": "^1.9|^2.1", 23 | "phpunit/phpunit": "^9.5.10|^10.5|^11.5.3", 24 | "larastan/larastan": "^2.4|^3.0", 25 | "laravel/pint": "^1.4" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Corcel\\WooCommerce\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Tests\\": "tests/", 35 | "Database\\Factories\\": "tests/database/factories/" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/larastan/larastan/extension.neon 3 | 4 | parameters: 5 | paths: 6 | - src 7 | - tests 8 | 9 | level: max 10 | 11 | noUnnecessaryCollectionCall: false 12 | 13 | stubFiles: 14 | - stubs/Corcel.stub 15 | 16 | ignoreErrors: 17 | - 18 | message: '#Access to an undefined property Faker\\Generator::\$[a-zA-Z]+?\.#' 19 | path: tests/* -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | src/ 18 | 19 | 20 | 21 | 22 | 23 | ./tests/Unit/ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Model/Builder/OrderBuilder.php: -------------------------------------------------------------------------------- 1 | status('cancelled'); 17 | } 18 | 19 | /** 20 | * Scope a query to only completed orders. 21 | */ 22 | public function completed(): PostBuilder 23 | { 24 | return $this->status('completed'); 25 | } 26 | 27 | /** 28 | * Scope a query to only failed orders. 29 | */ 30 | public function failed(): PostBuilder 31 | { 32 | return $this->status('failed'); 33 | } 34 | 35 | /** 36 | * Scope a query to only on hold orders. 37 | */ 38 | public function onHold(): PostBuilder 39 | { 40 | return $this->status('on-hold'); 41 | } 42 | 43 | /** 44 | * Scope a query to only pending orders. 45 | */ 46 | public function pending(): PostBuilder 47 | { 48 | return $this->status('pending'); 49 | } 50 | 51 | /** 52 | * Scope a query to only processing orders. 53 | */ 54 | public function processing(): PostBuilder 55 | { 56 | return $this->status('processing'); 57 | } 58 | 59 | /** 60 | * Scope a query to only refunded orders. 61 | */ 62 | public function refunded(): PostBuilder 63 | { 64 | return $this->status('refunded'); 65 | } 66 | 67 | /** 68 | * Scope a query to orders with given status. 69 | */ 70 | public function status($status): PostBuilder 71 | { 72 | $status = substr($status, 0, 3) === 'wc-' ? substr($status, 3) : $status; 73 | 74 | $builtin = [ 75 | 'cancelled', 76 | 'completed', 77 | 'failed', 78 | 'on-hold', 79 | 'pending', 80 | 'processing', 81 | 'refunded', 82 | ]; 83 | 84 | if (in_array($status, $builtin)) { 85 | $status = 'wc-'.$status; 86 | } 87 | 88 | return parent::status($status); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Model/Customer.php: -------------------------------------------------------------------------------- 1 | $orders 17 | */ 18 | class Customer extends User 19 | { 20 | use AddressesTrait; 21 | 22 | /** @use HasFactory */ 23 | use HasFactory; 24 | 25 | /** @use HasRelationsThroughMeta<\Illuminate\Database\Eloquent\Model, $this> */ 26 | use HasRelationsThroughMeta; 27 | 28 | /** 29 | * {@inheritDoc} 30 | * 31 | * @var array 32 | */ 33 | protected $appends = [ 34 | 'order_count', 35 | ]; 36 | 37 | /** 38 | * Create a new factory instance for the model. 39 | * 40 | * @return CustomerFactory 41 | */ 42 | protected static function newFactory() 43 | { 44 | return CustomerFactory::new(); 45 | } 46 | 47 | /** 48 | * Get order count attribute. 49 | */ 50 | protected function getOrderCountAttribute(): int 51 | { 52 | $count = $this->getMeta('_order_count'); 53 | 54 | return is_numeric($count) ? (int) $count : 0; 55 | } 56 | 57 | /** 58 | * Get the related orders. 59 | * 60 | * @return HasMany<\Illuminate\Database\Eloquent\Model, $this> 61 | */ 62 | public function orders(): HasMany 63 | { 64 | return $this->hasManyThroughMeta( 65 | Order::class, 66 | '_customer_user', 67 | 'post_id', 68 | 'ID' 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Model/Item.php: -------------------------------------------------------------------------------- 1 | */ 40 | use HasFactory; 41 | 42 | use MetaFields; 43 | 44 | /** 45 | * The model aliases. 46 | * 47 | * @var array|array> 48 | */ 49 | protected static $aliases = [ 50 | 'id' => 'order_item_id', 51 | 'name' => 'order_item_name', 52 | 'type' => 'order_item_type', 53 | 'product_id' => ['meta' => '_product_id'], 54 | 'variation_id' => ['meta' => '_variation_id'], 55 | ]; 56 | 57 | /** 58 | * {@inheritDoc} 59 | * 60 | * @var list 61 | */ 62 | protected $appends = [ 63 | 'quantity', 64 | 'tax_class', 65 | 'line_subtotal', 66 | 'line_subtotal_tax', 67 | 'line_total', 68 | 'line_tax', 69 | ]; 70 | 71 | /** 72 | * {@inheritDoc} 73 | * 74 | * @var string 75 | */ 76 | protected $table = 'woocommerce_order_items'; 77 | 78 | /** 79 | * {@inheritDoc} 80 | * 81 | * @var string 82 | */ 83 | protected $primaryKey = 'order_item_id'; 84 | 85 | /** 86 | * {@inheritDoc} 87 | * 88 | * @var bool 89 | */ 90 | public $timestamps = false; 91 | 92 | /** 93 | * Create a new factory instance for the model. 94 | * 95 | * @return ItemFactory 96 | */ 97 | protected static function newFactory() 98 | { 99 | return ItemFactory::new(); 100 | } 101 | 102 | /** 103 | * Get the line subtotal attribute. 104 | */ 105 | protected function getLineSubtotalAttribute(): ?string 106 | { 107 | $lineSubtotal = $this->getMeta('_line_subtotal'); 108 | 109 | return is_scalar($lineSubtotal) ? (string) $lineSubtotal : null; 110 | } 111 | 112 | /** 113 | * Get the line subtotal tax attribute. 114 | */ 115 | protected function getLineSubtotalTaxAttribute(): ?string 116 | { 117 | $lineSubtotalTax = $this->getMeta('_line_subtotal_tax'); 118 | 119 | return is_scalar($lineSubtotalTax) ? (string) $lineSubtotalTax : null; 120 | } 121 | 122 | /** 123 | * Get the line tax attribute. 124 | */ 125 | protected function getLineTaxAttribute(): ?string 126 | { 127 | $lineTax = $this->getMeta('_line_tax'); 128 | 129 | return is_scalar($lineTax) ? (string) $lineTax : null; 130 | } 131 | 132 | /** 133 | * Get the line total attribute. 134 | */ 135 | protected function getLineTotalAttribute(): ?string 136 | { 137 | $lineTotal = $this->getMeta('_line_total'); 138 | 139 | return is_scalar($lineTotal) ? (string) $lineTotal : null; 140 | } 141 | 142 | /** 143 | * Get the quantity attribute. 144 | */ 145 | protected function getQuantityAttribute(): ?string 146 | { 147 | $quantity = $this->getMeta('_qty'); 148 | 149 | return is_scalar($quantity) ? (string) $quantity : null; 150 | } 151 | 152 | /** 153 | * Get the tax class attribute. 154 | */ 155 | protected function getTaxClassAttribute(): ?string 156 | { 157 | $taxClass = $this->getMeta('_tax_class'); 158 | 159 | return is_scalar($taxClass) ? (string) $taxClass : null; 160 | } 161 | 162 | /** 163 | * {@inheritDoc} 164 | * 165 | * @return HasMany 166 | */ 167 | public function meta(): HasMany 168 | { 169 | return $this->hasMany(ItemMeta::class, 'order_item_id', 'order_item_id'); 170 | } 171 | 172 | /** 173 | * Get the related order. 174 | * 175 | * @return BelongsTo 176 | */ 177 | public function order(): BelongsTo 178 | { 179 | return $this->belongsTo(Order::class); 180 | } 181 | 182 | /** 183 | * Get the related product. 184 | * 185 | * @return BelongsTo 186 | */ 187 | public function product(): BelongsTo 188 | { 189 | return $this->belongsTo(Product::class); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/Model/Meta/ItemMeta.php: -------------------------------------------------------------------------------- 1 | $items 34 | */ 35 | class Order extends Post 36 | { 37 | use AddressesTrait; 38 | use Aliases; 39 | 40 | /** @use HasFactory */ 41 | use HasFactory; 42 | 43 | use MetaFields; 44 | 45 | /** 46 | * The model aliases. 47 | * 48 | * @var array> 49 | */ 50 | protected static $aliases = [ 51 | 'customer_id' => ['meta' => '_customer_user'], 52 | ]; 53 | 54 | /** 55 | * {@inheritDoc} 56 | * 57 | * @var array 58 | */ 59 | protected $appends = [ 60 | 'currency', 61 | 'total', 62 | 'shipping', 63 | 'tax', 64 | 'shipping_tax', 65 | 'status', 66 | 'date_completed', 67 | 'date_paid', 68 | 'payment', 69 | ]; 70 | 71 | /** 72 | * The post type slug. 73 | * 74 | * @var string 75 | */ 76 | protected $postType = 'shop_order'; 77 | 78 | /** 79 | * Create a new factory instance for the model. 80 | * 81 | * @return OrderFactory 82 | */ 83 | protected static function newFactory() 84 | { 85 | return OrderFactory::new(); 86 | } 87 | 88 | /** 89 | * {@inheritDoc} 90 | */ 91 | public function newEloquentBuilder($builder): OrderBuilder 92 | { 93 | return new OrderBuilder($builder); 94 | } 95 | 96 | /** 97 | * Get the currency attribute. 98 | */ 99 | protected function getCurrencyAttribute(): ?string 100 | { 101 | $currency = $this->getMeta('_order_currency'); 102 | 103 | return is_scalar($currency) ? (string) $currency : null; 104 | } 105 | 106 | /** 107 | * Get the total attribute. 108 | */ 109 | protected function getTotalAttribute(): ?string 110 | { 111 | $total = $this->getMeta('_order_total'); 112 | 113 | return is_scalar($total) ? (string) $total : null; 114 | } 115 | 116 | /** 117 | * Get the shipping attribute. 118 | */ 119 | protected function getShippingAttribute(): ?string 120 | { 121 | $shipping = $this->getMeta('_order_shipping'); 122 | 123 | return is_scalar($shipping) ? (string) $shipping : null; 124 | } 125 | 126 | /** 127 | * Get the tax attribute. 128 | */ 129 | protected function getTaxAttribute(): ?string 130 | { 131 | $tax = $this->getMeta('_order_tax'); 132 | 133 | return is_scalar($tax) ? (string) $tax : null; 134 | } 135 | 136 | /** 137 | * Get the shipping tax attribute. 138 | */ 139 | protected function getShippingTaxAttribute(): ?string 140 | { 141 | $shippingTax = $this->getMeta('_order_shipping_tax'); 142 | 143 | return is_scalar($shippingTax) ? (string) $shippingTax : null; 144 | } 145 | 146 | /** 147 | * Get the status attribute. 148 | */ 149 | public function getStatusAttribute(): string 150 | { 151 | $status = $this->post_status; 152 | 153 | return substr($status, 0, 3) === 'wc-' ? substr($status, 3) : $status; 154 | } 155 | 156 | /** 157 | * Get the completed date attribute. 158 | */ 159 | protected function getDateCompletedAttribute(): ?Carbon 160 | { 161 | $value = $this->getMeta('_date_completed'); 162 | 163 | if (is_numeric($value)) { 164 | return Carbon::createFromTimestamp($value); 165 | } 166 | 167 | /** 168 | * WooCommerce in version 2.6.x has stored completed date in 169 | * "_completed_date" meta field in MySQL datetime format. 170 | */ 171 | $value = $this->getMeta('_completed_date'); 172 | 173 | return is_string($value) 174 | ? Carbon::createFromFormat('Y-m-d H:i:s', $value) 175 | : null; 176 | } 177 | 178 | /** 179 | * Get the paid date attribute. 180 | */ 181 | public function getDatePaidAttribute(): ?Carbon 182 | { 183 | $value = $this->getMeta('_date_paid'); 184 | 185 | if (is_numeric($value)) { 186 | return Carbon::createFromTimestamp($value); 187 | } 188 | 189 | /** 190 | * WooCommerce in version 2.6.x has stored paid date in "_paid_date" 191 | * meta field in MySQL datetime format. 192 | */ 193 | $value = $this->getMeta('_paid_date'); 194 | 195 | return is_string($value) 196 | ? Carbon::createFromFormat('Y-m-d H:i:s', $value) 197 | : null; 198 | } 199 | 200 | /** 201 | * Get the payment attribute. 202 | */ 203 | public function getPaymentAttribute(): Payment 204 | { 205 | return new Payment($this); 206 | } 207 | 208 | /** 209 | * Get the related customer. 210 | * 211 | * @return BelongsTo 212 | */ 213 | public function customer(): BelongsTo 214 | { 215 | return $this->belongsTo(Customer::class); 216 | } 217 | 218 | /** 219 | * Get related items. 220 | * 221 | * @return HasMany 222 | */ 223 | public function items(): HasMany 224 | { 225 | return $this->hasMany(Item::class, 'order_id'); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/Model/Product.php: -------------------------------------------------------------------------------- 1 | $attributes 38 | * @property Collection $crosssells 39 | * @property Collection $upsells 40 | * @property BaseCollection $gallery 41 | * @property Collection $categories 42 | * @property Collection $items 43 | * @property Collection $productTypes 44 | * @property Collection $tags 45 | */ 46 | class Product extends Post 47 | { 48 | use Aliases; 49 | 50 | /** @use HasFactory */ 51 | use HasFactory; 52 | 53 | /** @use HasRelationsThroughMeta<\Illuminate\Database\Eloquent\Model, $this> */ 54 | use HasRelationsThroughMeta; 55 | 56 | use MetaFields; 57 | 58 | /** 59 | * Preloaded product attributes list. 60 | * 61 | * @var Collection 62 | */ 63 | protected static $productAttributes; 64 | 65 | /** 66 | * {@inheritDoc} 67 | * 68 | * @var array 69 | */ 70 | protected $appends = [ 71 | 'price', 72 | 'regular_price', 73 | 'sale_price', 74 | 'on_sale', 75 | 'sku', 76 | 'tax_status', 77 | 'weight', 78 | 'length', 79 | 'width', 80 | 'height', 81 | 'is_virtual', 82 | 'is_downloadable', 83 | 'stock', 84 | 'in_stock', 85 | ]; 86 | 87 | /** 88 | * The post type of model. 89 | * 90 | * @var string 91 | */ 92 | protected $postType = 'product'; 93 | 94 | /** 95 | * {@inheritDoc} 96 | */ 97 | protected static function boot() 98 | { 99 | parent::boot(); 100 | 101 | static::$productAttributes = ProductAttribute::all()->keyBy('attribute_name'); 102 | } 103 | 104 | /** 105 | * Create a new factory instance for the model. 106 | * 107 | * @return ProductFactory 108 | */ 109 | protected static function newFactory() 110 | { 111 | return ProductFactory::new(); 112 | } 113 | 114 | /** 115 | * Get the price attribute. 116 | */ 117 | protected function getPriceAttribute(): ?string 118 | { 119 | $price = $this->getMeta('_price'); 120 | 121 | return is_scalar($price) ? (string) $price : null; 122 | } 123 | 124 | /** 125 | * Get the regular price attribute. 126 | */ 127 | protected function getRegularPriceAttribute(): ?string 128 | { 129 | $regularPrice = $this->getMeta('_regular_price'); 130 | 131 | return is_scalar($regularPrice) ? (string) $regularPrice : null; 132 | } 133 | 134 | /** 135 | * Get the sale price attribute 136 | */ 137 | protected function getSalePriceAttribute(): ?string 138 | { 139 | $salePrice = $this->getMeta('_sale_price'); 140 | 141 | return is_scalar($salePrice) ? (string) $salePrice : null; 142 | } 143 | 144 | /** 145 | * Get the on sale attribute 146 | */ 147 | protected function getOnSaleAttribute(): bool 148 | { 149 | return ! empty($this->sale_price) && $this->sale_price < $this->regular_price; 150 | } 151 | 152 | /** 153 | * Get the SKU attribute. 154 | */ 155 | protected function getSkuAttribute(): ?string 156 | { 157 | $sku = $this->getMeta('_sku'); 158 | 159 | return is_scalar($sku) ? (string) $sku : null; 160 | } 161 | 162 | /** 163 | * Get the tax status attribute. 164 | */ 165 | protected function getTaxStatusAttribute(): ?string 166 | { 167 | $taxStatus = $this->getMeta('_tax_status'); 168 | 169 | return is_scalar($taxStatus) ? (string) $taxStatus : null; 170 | } 171 | 172 | /** 173 | * Get the is taxable attribute. 174 | */ 175 | public function getIsTaxableAttribute(): bool 176 | { 177 | return $this->tax_status === 'taxable'; 178 | } 179 | 180 | /** 181 | * Get the weight attribute. 182 | */ 183 | protected function getWeightAttribute(): ?string 184 | { 185 | $weight = $this->getMeta('_weight'); 186 | 187 | return is_scalar($weight) ? (string) $weight : null; 188 | } 189 | 190 | /** 191 | * Get the length attribute. 192 | */ 193 | protected function getLengthAttribute(): ?string 194 | { 195 | $length = $this->getMeta('_length'); 196 | 197 | return is_scalar($length) ? (string) $length : null; 198 | } 199 | 200 | /** 201 | * Get the width attribute. 202 | */ 203 | protected function getWidthAttribute(): ?string 204 | { 205 | $width = $this->getMeta('_width'); 206 | 207 | return is_scalar($width) ? (string) $width : null; 208 | } 209 | 210 | /** 211 | * Get the height attribute. 212 | */ 213 | protected function getHeightAttribute(): ?string 214 | { 215 | $height = $this->getMeta('_height'); 216 | 217 | return is_scalar($height) ? (string) $height : null; 218 | } 219 | 220 | /** 221 | * Get the is virtual attribute. 222 | */ 223 | protected function getIsVirtualAttribute(): bool 224 | { 225 | return $this->getMeta('_virtual') === 'yes'; 226 | } 227 | 228 | /** 229 | * Get the is downloadable attribute. 230 | */ 231 | protected function getIsDownloadableAttribute(): bool 232 | { 233 | return $this->getMeta('_downloadable') === 'yes'; 234 | } 235 | 236 | /** 237 | * Get the stock attribute. 238 | */ 239 | protected function getStockAttribute(): ?string 240 | { 241 | $stock = $this->getMeta('_stock'); 242 | 243 | return is_scalar($stock) ? (string) $stock : null; 244 | } 245 | 246 | /** 247 | * Get the in stock attribute. 248 | */ 249 | protected function getInStockAttribute(): bool 250 | { 251 | return $this->getMeta('_stock_status') === 'instock'; 252 | } 253 | 254 | /** 255 | * Get the product attributes attribute. 256 | * 257 | * @return BaseCollection 258 | */ 259 | protected function getAttributesAttribute(): BaseCollection 260 | { 261 | $taxonomies = $this->taxonomies; 262 | 263 | return $taxonomies 264 | ->filter(function ($taxonomy) { 265 | return isset($taxonomy->taxonomy) 266 | && is_string($taxonomy->taxonomy) 267 | && Str::startsWith($taxonomy->taxonomy, 'pa_'); 268 | }) 269 | ->groupBy('taxonomy') 270 | ->map(function ($taxonomy, $taxonomyName) { 271 | $attributeName = substr($taxonomyName, 3); 272 | /** @var \Corcel\WooCommerce\Model\ProductAttribute */ 273 | $attribute = static::$productAttributes->get($attributeName); 274 | $attribute->setTerms($taxonomy->pluck('term')); 275 | 276 | return $attribute; 277 | }) 278 | ->keyBy('attribute_name'); 279 | } 280 | 281 | /** 282 | * Get the cross-sells attribute. 283 | * 284 | * @return Collection 285 | */ 286 | protected function getCrosssellsAttribute(): Collection 287 | { 288 | $crosssells = $this->getMeta('_crosssell_ids'); 289 | 290 | if (! is_string($crosssells)) { 291 | return static::newCollection(); 292 | } 293 | 294 | $crosssells = unserialize($crosssells); 295 | 296 | if (empty($crosssells)) { 297 | return static::newCollection(); 298 | } 299 | 300 | /** @var Collection */ 301 | return static::query()->whereIn('ID', $crosssells)->get(); 302 | } 303 | 304 | /** 305 | * Get the up-sells attribute. 306 | * 307 | * @return Collection 308 | */ 309 | public function getUpsellsAttribute(): Collection 310 | { 311 | $upsells = $this->getMeta('_upsell_ids'); 312 | 313 | if (! is_string($upsells)) { 314 | return static::newCollection(); 315 | } 316 | 317 | $upsells = unserialize($upsells); 318 | 319 | if (empty($upsells)) { 320 | return static::newCollection(); 321 | } 322 | 323 | /** @var Collection */ 324 | return static::query()->whereIn('ID', $upsells)->get(); 325 | } 326 | 327 | /** 328 | * Get the gallery attribute. 329 | * 330 | * @return BaseCollection 331 | */ 332 | public function getGalleryAttribute(): BaseCollection 333 | { 334 | $gallery = new BaseCollection; 335 | 336 | if (isset($this->thumbnail->attachment) && $this->thumbnail->attachment instanceof Attachment) { 337 | $gallery->push($this->thumbnail->attachment); 338 | } 339 | 340 | $attachmentsId = $this->getMeta('_product_image_gallery'); 341 | 342 | if (! is_string($attachmentsId) || empty($attachmentsId)) { 343 | return $gallery; 344 | } 345 | 346 | $attachmentsId = explode(',', $attachmentsId); 347 | $attachments = Attachment::query()->whereIn('ID', $attachmentsId)->get(); 348 | 349 | return $gallery->merge($attachments); 350 | } 351 | 352 | /** 353 | * Get the type attribute. 354 | */ 355 | protected function getTypeAttribute(): ?string 356 | { 357 | /** @var BaseCollection */ 358 | $productTypeNames = $this->productTypes->pluck('term.name'); 359 | 360 | return $productTypeNames->first(); 361 | } 362 | 363 | /** 364 | * Get the related categories. 365 | * 366 | * @return BelongsToMany 367 | */ 368 | public function categories(): BelongsToMany 369 | { 370 | return $this->belongsToMany( 371 | ProductCategory::class, 372 | 'term_relationships', 373 | 'object_id', 374 | 'term_taxonomy_id' 375 | ); 376 | } 377 | 378 | /** 379 | * Get the related items. 380 | * 381 | * @return HasMany<\Illuminate\Database\Eloquent\Model, $this> 382 | */ 383 | public function items(): HasMany 384 | { 385 | return $this->hasManyThroughMeta( 386 | Item::class, 387 | '_product_id', 388 | 'order_item_id', 389 | 'order_item_id' 390 | ); 391 | } 392 | 393 | /** 394 | * Get the related product types. 395 | * 396 | * @return BelongsToMany 397 | */ 398 | public function productTypes(): BelongsToMany 399 | { 400 | return $this->belongsToMany( 401 | ProductType::class, 402 | 'term_relationships', 403 | 'object_id', 404 | 'term_taxonomy_id' 405 | ); 406 | } 407 | 408 | /** 409 | * Get the related tags. 410 | * 411 | * @return BelongsToMany 412 | */ 413 | public function tags(): BelongsToMany 414 | { 415 | return $this->belongsToMany( 416 | ProductTag::class, 417 | 'term_relationships', 418 | 'object_id', 419 | 'term_taxonomy_id' 420 | ); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /src/Model/ProductAttribute.php: -------------------------------------------------------------------------------- 1 | 33 | */ 34 | public Collection $terms; 35 | 36 | /** 37 | * The model aliases. 38 | * 39 | * @var array 40 | */ 41 | protected static $aliases = [ 42 | 'id' => 'attribute_id', 43 | 'slug' => 'attribute_name', 44 | 'name' => 'attribute_label', 45 | 'type' => 'attribute_type', 46 | 'order_by' => 'attribute_orderby', 47 | 'public' => 'attribute_public', 48 | ]; 49 | 50 | /** 51 | * {@inheritDoc} 52 | * 53 | * @var array 54 | */ 55 | protected $casts = [ 56 | 'attribute_public' => 'bool', 57 | ]; 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | protected $primaryKey = 'attribute_id'; 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | protected $table = 'woocommerce_attribute_taxonomies'; 68 | 69 | /** 70 | * Set the product terms. 71 | * 72 | * @param Collection $terms 73 | */ 74 | public function setTerms(Collection $terms): void 75 | { 76 | $this->terms = $terms; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Model/ProductCategory.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Address implements Arrayable, Jsonable 18 | { 19 | /** 20 | * The model instance. 21 | */ 22 | protected Model $model; 23 | 24 | /** 25 | * The address type. 26 | */ 27 | protected string $type; 28 | 29 | /** 30 | * The address attributes. 31 | * 32 | * @var array 33 | */ 34 | protected array $attributes = []; 35 | 36 | /** 37 | * The address constructor. 38 | */ 39 | public function __construct(Model $model, string $type) 40 | { 41 | $this->model = $model; 42 | $this->type = $type; 43 | 44 | $this->parseAttributes(); 45 | } 46 | 47 | /** 48 | * Parse address attributes. 49 | */ 50 | protected function parseAttributes(): void 51 | { 52 | foreach ($this->attributeKeys() as $key) { 53 | $metaKey = $this->getMetaKey($key); 54 | 55 | // @phpstan-ignore-next-line 56 | $this->attributes[$key] = $this->model->getMeta($metaKey); 57 | } 58 | } 59 | 60 | /** 61 | * List of the attribute keys. 62 | * 63 | * @return array 64 | */ 65 | protected function attributeKeys(): array 66 | { 67 | $keys = [ 68 | 'first_name', 69 | 'last_name', 70 | 'company', 71 | 'address_1', 72 | 'address_2', 73 | 'city', 74 | 'state', 75 | 'postcode', 76 | 'country', 77 | ]; 78 | 79 | if ($this->type === 'billing') { 80 | $keys = array_merge($keys, [ 81 | 'email', 82 | 'phone', 83 | ]); 84 | } 85 | 86 | return $keys; 87 | } 88 | 89 | /** 90 | * Get meta key for given attribute name. 91 | */ 92 | protected function getMetaKey(string $key): string 93 | { 94 | $pattern = $this->metaKeyPattern(); 95 | 96 | return sprintf($pattern, $this->type, $key); 97 | } 98 | 99 | /** 100 | * Get meta key pattern based on model. 101 | */ 102 | protected function metaKeyPattern(): string 103 | { 104 | if ($this->model instanceof Customer) { 105 | return '%s_%s'; 106 | } elseif ($this->model instanceof Order) { 107 | return '_%s_%s'; 108 | } 109 | 110 | throw new InvalidArgumentException(sprintf( 111 | 'Model "%s" cannot have address.', 112 | get_class($this->model) 113 | )); 114 | } 115 | 116 | /** 117 | * {@inheritDoc} 118 | * 119 | * @return array 120 | */ 121 | public function toArray(): array 122 | { 123 | return $this->attributes; 124 | } 125 | 126 | /** 127 | * {@inheritDoc} 128 | * 129 | * @param int $options 130 | */ 131 | public function toJson($options = 0): string 132 | { 133 | $json = json_encode($this->toArray(), $options); 134 | 135 | if ($json === false || json_last_error() !== JSON_ERROR_NONE) { 136 | throw new InvalidArgumentException('An error occured while converting order address to JSON.'); 137 | } 138 | 139 | return $json; 140 | } 141 | 142 | /** 143 | * Magic method to get address attributes. 144 | * 145 | * @return mixed 146 | */ 147 | public function __get(string $key) 148 | { 149 | if (array_key_exists($key, $this->attributes)) { 150 | return $this->attributes[$key]; 151 | } 152 | 153 | throw new InvalidArgumentException("Property {$key} does not exists."); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Support/BillingAddress.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Payment implements Arrayable, Jsonable 16 | { 17 | /** 18 | * The payment method. 19 | */ 20 | public ?string $method = null; 21 | 22 | /** 23 | * The payment method title. 24 | */ 25 | public ?string $method_title = null; 26 | 27 | /** 28 | * The payment transation identificator. 29 | */ 30 | public ?string $transaction_id = null; 31 | 32 | /** 33 | * The payment constructor. 34 | */ 35 | public function __construct(Order $order) 36 | { 37 | $method = $order->getMeta('_payment_method'); 38 | $methodTitle = $order->getMeta('_payment_method_title'); 39 | $transactionId = $order->getMeta('_transaction_id'); 40 | 41 | $this->method = is_scalar($method) ? (string) $method : null; 42 | $this->method_title = is_scalar($methodTitle) ? (string) $methodTitle : null; 43 | $this->transaction_id = is_scalar($transactionId) ? (string) $transactionId : null; 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | * 49 | * @return array 50 | */ 51 | public function toArray(): array 52 | { 53 | return [ 54 | 'method' => $this->method, 55 | 'method_title' => $this->method_title, 56 | 'transaction_id' => $this->transaction_id, 57 | ]; 58 | } 59 | 60 | /** 61 | * {@inheritDoc} 62 | * 63 | * @param int $options 64 | */ 65 | public function toJson($options = 0): string 66 | { 67 | $json = json_encode($this->toArray(), $options); 68 | 69 | if ($json === false || json_last_error() !== JSON_ERROR_NONE) { 70 | throw new InvalidArgumentException('An error occured while converting order payment to JSON.'); 71 | } 72 | 73 | return $json; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Support/ShippingAddress.php: -------------------------------------------------------------------------------- 1 | appends = array_merge($this->appends, [ 22 | 'billing_address', 23 | 'shipping_address', 24 | ]); 25 | } 26 | 27 | /** 28 | * Get the billing address attribute. 29 | */ 30 | protected function getBillingAddressAttribute(): BillingAddress 31 | { 32 | return new BillingAddress($this); 33 | } 34 | 35 | /** 36 | * Get the shipping address attribute. 37 | */ 38 | protected function getShippingAddressAttribute(): ShippingAddress 39 | { 40 | return new ShippingAddress($this); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Traits/HasRelationsThroughMeta.php: -------------------------------------------------------------------------------- 1 | $related 26 | * @return HasMany 27 | * 28 | * @throws InvalidArgumentException 29 | * @throws LogicException 30 | */ 31 | public function hasManyThroughMeta(string $related, string $metaKey, ?string $foreignKey = null, ?string $localKey = null): HasMany 32 | { 33 | /** @var Model */ 34 | $model = $this->newRelatedInstance($related); 35 | $meta = $this->metaInstance($model); 36 | 37 | $foreignKey = $foreignKey ?: $this->getForeignKey(); 38 | $localKey = $localKey ?: $this->getKeyName(); 39 | 40 | return $this->hasMany($related, $meta->qualifyColumn('meta_value')) 41 | ->leftJoin($meta->getTable(), $this->joinClause( 42 | $model->qualifyColumn($localKey), 43 | $meta->qualifyColumn($foreignKey), 44 | $metaKey 45 | )) 46 | ->select($model->qualifyColumn('*')); 47 | } 48 | 49 | /** 50 | * Make meta model instance. 51 | * 52 | * @throws InvalidArgumentException 53 | * @throws LogicException 54 | */ 55 | private function metaInstance(Model $model): Meta 56 | { 57 | if (! method_exists($model, 'meta')) { 58 | throw new LogicException(sprintf( 59 | 'The model "%s" must have defined "meta" method. Adding "%s" trait will likely solve this problem.', 60 | get_class($model), 61 | MetaFields::class 62 | )); 63 | } 64 | 65 | /** @var \Illuminate\Database\Eloquent\Relations\HasMany */ 66 | $relation = $model->meta(); 67 | $meta = $relation->getRelated(); 68 | 69 | if (! $meta instanceof Meta) { 70 | throw new InvalidArgumentException(sprintf( 71 | 'The meta method of "%s" model must extends "%s" model.', 72 | get_class($meta), 73 | Meta::class 74 | )); 75 | } 76 | 77 | return $meta; 78 | } 79 | 80 | /** 81 | * Build join clause between model and meta tables. 82 | */ 83 | private function joinClause(string $localKey, string $foreignKey, string $metaKey): Closure 84 | { 85 | return function (JoinClause $join) use ($localKey, $foreignKey, $metaKey) { 86 | $join->on($localKey, '=', $foreignKey) 87 | ->where('meta_key', '=', $metaKey); 88 | }; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/WooCommerce.php: -------------------------------------------------------------------------------- 1 | loadMigrationsFrom([ 19 | '--database' => 'wp', 20 | '--realpath' => true, 21 | '--path' => __DIR__.'/database/migrations', 22 | ]); 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | * 28 | * @param \Illuminate\Foundation\Application $app 29 | */ 30 | protected function getEnvironmentSetUp($app): void 31 | { 32 | $this->configureDatabaseConfig($app); 33 | } 34 | 35 | /** 36 | * Configure database. 37 | * 38 | * @param \Illuminate\Foundation\Application $app 39 | */ 40 | private function configureDatabaseConfig($app): void 41 | { 42 | $app['config']->set('database.connections.wp', [ 43 | 'driver' => 'sqlite', 44 | 'database' => ':memory:', 45 | 'prefix' => 'wp_', 46 | ]); 47 | 48 | $app['config']->set('database.default', 'wp'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Model/CustomerTest.php: -------------------------------------------------------------------------------- 1 | createCustomer(); 16 | $customer->createMeta([ 17 | '_order_count' => 10, 18 | ]); 19 | 20 | $this->assertSame(10, $customer->order_count); 21 | } 22 | 23 | public function test_array_has_appended_values(): void 24 | { 25 | $customer = $this->createCustomer(); 26 | 27 | $this->assertArrayHasKey('order_count', $customer->toArray()); 28 | } 29 | 30 | public function test_related_orders(): void 31 | { 32 | $customer = $this->createCustomer(); 33 | 34 | /** @var Order */ 35 | $order = Order::factory()->create(); 36 | $order->createMeta('_customer_user', $customer->ID); 37 | 38 | $this->assertTrue($customer->orders()->get()->first()->is($order)); // @phpstan-ignore-line 39 | 40 | /** @var Order */ 41 | $order = Order::factory()->create(); 42 | $order->createMeta('_customer_user', $customer->ID); 43 | 44 | $this->assertSame(2, $customer->orders()->count()); 45 | } 46 | 47 | private function createCustomer(): Customer 48 | { 49 | /** @var Customer */ 50 | return Customer::factory()->create(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Unit/Model/ItemTest.php: -------------------------------------------------------------------------------- 1 | createItem(); 17 | $item->createMeta('_line_subtotal', '9.99'); 18 | 19 | $this->assertSame('9.99', $item->line_subtotal); 20 | } 21 | 22 | public function test_line_subtotal_tax_property(): void 23 | { 24 | $item = $this->createItem(); 25 | $item->createMeta('_line_subtotal_tax', '8.88'); 26 | 27 | $this->assertSame('8.88', $item->line_subtotal_tax); 28 | } 29 | 30 | public function test_line_tax_property(): void 31 | { 32 | $item = $this->createItem(); 33 | $item->createMeta('_line_tax', '7.77'); 34 | 35 | $this->assertSame('7.77', $item->line_tax); 36 | } 37 | 38 | public function test_line_total_property(): void 39 | { 40 | $item = $this->createItem(); 41 | $item->createMeta('_line_total', '6.66'); 42 | 43 | $this->assertSame('6.66', $item->line_total); 44 | } 45 | 46 | public function test_quantity_property(): void 47 | { 48 | $item = $this->createItem(); 49 | $item->createMeta('_qty', '5'); 50 | 51 | $this->assertSame('5', $item->quantity); 52 | } 53 | 54 | public function test_tax_class_property(): void 55 | { 56 | $item = $this->createItem(); 57 | $item->createMeta('_tax_class', 'standard'); 58 | 59 | $this->assertSame('standard', $item->tax_class); 60 | } 61 | 62 | public function test_related_order(): void 63 | { 64 | /** @var Order */ 65 | $order = Order::factory()->create(); 66 | $item = $this->createItem(['order_id' => $order->ID]); 67 | 68 | $this->assertTrue($item->order->is($order)); 69 | } 70 | 71 | public function test_related_product(): void 72 | { 73 | /** @var \Corcel\WooCommerce\Model\Product */ 74 | $product = Product::factory()->create(); 75 | 76 | $item = $this->createItem(); 77 | $item->createMeta('_product_id', $product->ID); 78 | 79 | $this->assertTrue($item->product->is($product)); 80 | } 81 | 82 | /** 83 | * @param array $attributes 84 | */ 85 | private function createItem(array $attributes = []): Item 86 | { 87 | /** @var Item */ 88 | $item = Item::factory()->create($attributes); 89 | 90 | return $item; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/Unit/Model/OrderTest.php: -------------------------------------------------------------------------------- 1 | createOrder(); 21 | $order->createMeta('_order_currency', 'USD'); 22 | 23 | $this->assertSame('USD', $order->currency); 24 | } 25 | 26 | public function test_total_property(): void 27 | { 28 | $order = $this->createOrder(); 29 | $order->createMeta('_order_total', '9.99'); 30 | 31 | $this->assertSame('9.99', $order->total); 32 | } 33 | 34 | public function test_shipping_property(): void 35 | { 36 | $order = $this->createOrder(); 37 | $order->createMeta('_order_shipping', '8.88'); 38 | 39 | $this->assertSame('8.88', $order->shipping); 40 | } 41 | 42 | public function test_tax_property(): void 43 | { 44 | $order = $this->createOrder(); 45 | $order->createMeta('_order_tax', '7.77'); 46 | 47 | $this->assertSame('7.77', $order->tax); 48 | } 49 | 50 | public function test_shipping_tax_property(): void 51 | { 52 | $order = $this->createOrder(); 53 | $order->createMeta('_order_shipping_tax', '6.66'); 54 | 55 | $this->assertSame('6.66', $order->shipping_tax); 56 | } 57 | 58 | public function test_status_property(): void 59 | { 60 | $order = $this->createOrder([ 61 | 'post_status' => 'wc-refunded', 62 | ]); 63 | 64 | $this->assertSame('refunded', $order->status); 65 | } 66 | 67 | public function test_date_completed_property(): void 68 | { 69 | $order = $this->createOrder(); 70 | $order->createMeta('_date_completed', 1577836800); // '2020-01-01 00:00:00' 71 | 72 | $this->assertInstanceOf(Carbon::class, $order->date_completed); 73 | $this->assertSame('2020-01-01 00:00:00', $order->date_completed->format('Y-m-d H:i:s')); 74 | } 75 | 76 | public function test_date_paid_property(): void 77 | { 78 | $order = $this->createOrder(); 79 | $order->createMeta('_date_paid', 1577840400); // '2020-01-01 01:00:00' 80 | 81 | $this->assertInstanceOf(Carbon::class, $order->date_paid); 82 | $this->assertSame('2020-01-01 01:00:00', $order->date_paid->format('Y-m-d H:i:s')); 83 | } 84 | 85 | public function test_deprecated_date_formats(): void 86 | { 87 | $order = $this->createOrder(); 88 | $order->createMeta('_completed_date', '2020-01-01 00:00:00'); 89 | $order->createMeta('_paid_date', '2020-01-01 01:00:00'); 90 | 91 | $this->assertInstanceOf(Carbon::class, $order->date_completed); 92 | $this->assertSame('2020-01-01 00:00:00', $order->date_completed->format('Y-m-d H:i:s')); 93 | 94 | $this->assertInstanceOf(Carbon::class, $order->date_paid); 95 | $this->assertSame('2020-01-01 01:00:00', $order->date_paid->format('Y-m-d H:i:s')); 96 | } 97 | 98 | public function test_payment_property(): void 99 | { 100 | $order = $this->createOrder(); 101 | 102 | $this->assertInstanceOf(Payment::class, $order->payment); 103 | } 104 | 105 | public function test_billing_address_property(): void 106 | { 107 | $order = $this->createOrder(); 108 | 109 | $this->assertInstanceOf(BillingAddress::class, $order->billing_address); 110 | } 111 | 112 | public function test_shipping_address_property(): void 113 | { 114 | $order = $this->createOrder(); 115 | 116 | $this->assertInstanceOf(ShippingAddress::class, $order->shipping_address); 117 | } 118 | 119 | public function test_array_has_appended_values(): void 120 | { 121 | /** @var Order */ 122 | $order = Order::factory()->create(); 123 | $array = $order->toArray(); 124 | 125 | $this->assertArrayHasKey('currency', $array); 126 | $this->assertArrayHasKey('total', $array); 127 | $this->assertArrayHasKey('shipping', $array); 128 | $this->assertArrayHasKey('tax', $array); 129 | $this->assertArrayHasKey('shipping_tax', $array); 130 | $this->assertArrayHasKey('status', $array); 131 | $this->assertArrayHasKey('date_completed', $array); 132 | $this->assertArrayHasKey('date_paid', $array); 133 | $this->assertArrayHasKey('payment', $array); 134 | $this->assertArrayHasKey('billing_address', $array); 135 | $this->assertArrayHasKey('shipping_address', $array); 136 | } 137 | 138 | public function test_related_customer(): void 139 | { 140 | /** @var Customer */ 141 | $customer = Customer::factory()->create(); 142 | 143 | $order = $this->createOrder(); 144 | $order->createMeta('_customer_user', $customer->ID); 145 | 146 | /** @var Customer */ 147 | $orderCustomer = $order->customer; 148 | 149 | $this->assertTrue($orderCustomer->is($customer)); 150 | } 151 | 152 | public function test_guest_order(): void 153 | { 154 | $order = $this->createOrder(); 155 | 156 | $this->assertNull($order->customer); 157 | } 158 | 159 | public function test_related_items(): void 160 | { 161 | $order = $this->createOrder(); 162 | 163 | $item = Item::factory()->count(3)->create(['order_id' => $order->ID]); 164 | 165 | $this->assertSame(3, $order->items->count()); 166 | } 167 | 168 | /** 169 | * @param array $attributes 170 | */ 171 | private function createOrder(array $attributes = []): Order 172 | { 173 | /** @var Order */ 174 | $order = Order::factory()->create($attributes); 175 | 176 | return $order; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tests/Unit/Model/ProductAttributeTest.php: -------------------------------------------------------------------------------- 1 | setTerms(new Collection); 17 | 18 | $this->assertInstanceOf(Collection::class, $productAttribute->terms); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Unit/Model/ProductTest.php: -------------------------------------------------------------------------------- 1 | createProduct(); 16 | $product->createMeta('_price', '9.99'); 17 | 18 | $this->assertSame('9.99', $product->price); 19 | } 20 | 21 | public function test_regular_price_property(): void 22 | { 23 | $product = $this->createProduct(); 24 | $product->createMeta('_regular_price', '8.88'); 25 | 26 | $this->assertSame('8.88', $product->regular_price); 27 | } 28 | 29 | public function test_sale_price_property(): void 30 | { 31 | $product = $this->createProduct(); 32 | $product->createMeta('_sale_price', '7.77'); 33 | 34 | $this->assertSame('7.77', $product->sale_price); 35 | } 36 | 37 | public function test_on_sale_property(): void 38 | { 39 | $product = $this->createProduct(); 40 | $product->createMeta('_regular_price', '6.66'); 41 | $product->createMeta('_sale_price', '5.55'); 42 | 43 | $this->assertTrue($product->on_sale); 44 | 45 | $product = $this->createProduct(); 46 | $product->createMeta('_regular_price', '4.44'); 47 | 48 | $this->assertFalse($product->on_sale); 49 | } 50 | 51 | public function test_sku_property(): void 52 | { 53 | $product = $this->createProduct(); 54 | $product->createMeta('_sku', 'UNIQUE'); 55 | 56 | $this->assertSame('UNIQUE', $product->sku); 57 | } 58 | 59 | public function test_tax_status_property(): void 60 | { 61 | $product = $this->createProduct(); 62 | $product->createMeta('_tax_status', 'taxable'); 63 | 64 | $this->assertSame('taxable', $product->tax_status); 65 | } 66 | 67 | public function test_is_taxable_property(): void 68 | { 69 | $product = $this->createProduct(); 70 | $product->createMeta('_tax_status', 'taxable'); 71 | 72 | $this->assertTrue($product->is_taxable); 73 | 74 | $product = $this->createProduct(); 75 | $product->createMeta('_tax_status', ''); 76 | 77 | $this->assertFalse($product->is_taxable); 78 | } 79 | 80 | public function test_weight_property(): void 81 | { 82 | $product = $this->createProduct(); 83 | $product->createMeta('_weight', '3.33'); 84 | 85 | $this->assertSame('3.33', $product->weight); 86 | } 87 | 88 | public function test_length_property(): void 89 | { 90 | $product = $this->createProduct(); 91 | $product->createMeta('_length', '2.22'); 92 | 93 | $this->assertSame('2.22', $product->length); 94 | } 95 | 96 | public function test_width_property(): void 97 | { 98 | $product = $this->createProduct(); 99 | $product->createMeta('_width', '1.11'); 100 | 101 | $this->assertSame('1.11', $product->width); 102 | } 103 | 104 | public function test_height_property(): void 105 | { 106 | $product = $this->createProduct(); 107 | $product->createMeta('_height', '0.00'); 108 | 109 | $this->assertSame('0.00', $product->height); 110 | } 111 | 112 | public function test_is_virtual_property(): void 113 | { 114 | $product = $this->createProduct(); 115 | $product->createMeta('_virtual', 'yes'); 116 | 117 | $this->assertTrue($product->is_virtual); 118 | 119 | $product = $this->createProduct(); 120 | $product->createMeta('_virtual', 'no'); 121 | 122 | $this->assertFalse($product->is_virtual); 123 | } 124 | 125 | public function test_is_downloadable_property(): void 126 | { 127 | $product = $this->createProduct(); 128 | $product->createMeta('_downloadable', 'yes'); 129 | 130 | $this->assertTrue($product->is_downloadable); 131 | 132 | $product = $this->createProduct(); 133 | $product->createMeta('_downloadable', 'no'); 134 | 135 | $this->assertFalse($product->is_downloadable); 136 | } 137 | 138 | public function test_stock_property(): void 139 | { 140 | $product = $this->createProduct(); 141 | $product->createMeta('_stock', '5'); 142 | 143 | $this->assertSame('5', $product->stock); 144 | } 145 | 146 | public function test_in_stock_property(): void 147 | { 148 | $product = $this->createProduct(); 149 | $product->createMeta('_stock_status', 'instock'); 150 | 151 | $this->assertTrue($product->in_stock); 152 | 153 | $product = $this->createProduct(); 154 | $product->createMeta('_stock_status', ''); 155 | 156 | $this->assertFalse($product->in_stock); 157 | } 158 | 159 | public function test_crosssells_property(): void 160 | { 161 | /** @var \Illuminate\Database\Eloquent\Collection */ 162 | $crosssellProducts = Product::factory()->count(2)->create(); 163 | 164 | $product = $this->createProduct(); 165 | $product->createMeta('_crosssell_ids', serialize($crosssellProducts->pluck('ID')->toArray())); 166 | 167 | $this->assertSame(2, $product->crosssells->count()); 168 | // @phpstan-ignore-next-line 169 | $this->assertTrue($product->crosssells->first()->is($crosssellProducts->first())); 170 | } 171 | 172 | public function test_empty_crosssells(): void 173 | { 174 | $product = $this->createProduct(); 175 | $product->createMeta('_crosssell_ids', serialize([])); 176 | 177 | $this->assertSame(0, $product->crosssells->count()); 178 | } 179 | 180 | public function test_upsells_property(): void 181 | { 182 | /** @var \Illuminate\Database\Eloquent\Collection */ 183 | $upsellProducts = Product::factory()->count(3)->create(); 184 | 185 | $product = $this->createProduct(); 186 | $product->createMeta('_upsell_ids', serialize($upsellProducts->pluck('ID')->toArray())); 187 | 188 | $this->assertSame(3, $product->upsells->count()); 189 | // @phpstan-ignore-next-line 190 | $this->assertTrue($product->upsells->first()->is($upsellProducts->first())); 191 | } 192 | 193 | public function test_empty_upsells(): void 194 | { 195 | $product = $this->createProduct(); 196 | $product->createMeta('_upsell_ids', serialize([])); 197 | 198 | $this->assertSame(0, $product->upsells->count()); 199 | } 200 | 201 | public function test_related_items(): void 202 | { 203 | $product = $this->createProduct(); 204 | 205 | /** @var Item */ 206 | $firstItem = Item::factory()->create(); 207 | $firstItem->createMeta('_product_id', $product->ID); 208 | 209 | /** @var Item */ 210 | $secondItem = Item::factory()->create(); 211 | $secondItem->createMeta('_product_id', $product->ID); 212 | 213 | $this->assertSame(2, $product->items->count()); 214 | // @phpstan-ignore-next-line 215 | $this->assertTrue($product->items->first()->is($firstItem)); 216 | } 217 | 218 | private function createProduct(): Product 219 | { 220 | /** @var Product */ 221 | $product = Product::factory()->create(); 222 | 223 | return $product; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /tests/Unit/Support/AddressTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 18 | 19 | /** @var Item */ 20 | $model = Item::factory()->create(); 21 | 22 | new Address($model, 'billing'); 23 | } 24 | 25 | public function test_invalid_json(): void 26 | { 27 | $this->expectException(InvalidArgumentException::class); 28 | 29 | /** @var Order */ 30 | $order = Order::factory()->create(); 31 | $address = new class($order, 'billing') extends Address 32 | { 33 | public function toArray(): array 34 | { 35 | return [ 36 | 'invalid' => fopen('php://input', 'r'), 37 | ]; 38 | } 39 | }; 40 | 41 | $address->toJson(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/Support/BillingAddressTest.php: -------------------------------------------------------------------------------- 1 | 'John', 17 | '_billing_last_name' => 'Doe', 18 | '_billing_company' => 'ACME corp.', 19 | '_billing_address_1' => 'Example Street 10', 20 | '_billing_address_2' => 'Test Address', 21 | '_billing_city' => 'Los Angeles', 22 | '_billing_state' => 'California', 23 | '_billing_postcode' => '00000', 24 | '_billing_country' => 'USA', 25 | '_billing_email' => 'john@doe.com', 26 | '_billing_phone' => '00-00-000-000', 27 | ]; 28 | 29 | private const CUSTOMER_META_FIELDS = [ 30 | 'billing_first_name' => 'John', 31 | 'billing_last_name' => 'Doe', 32 | 'billing_company' => 'ACME corp.', 33 | 'billing_address_1' => 'Example Street 10', 34 | 'billing_address_2' => 'Test Address', 35 | 'billing_city' => 'Los Angeles', 36 | 'billing_state' => 'California', 37 | 'billing_postcode' => '00000', 38 | 'billing_country' => 'USA', 39 | 'billing_email' => 'john@doe.com', 40 | 'billing_phone' => '00-00-000-000', 41 | ]; 42 | 43 | public function test_order_billing_address_properties(): void 44 | { 45 | $billingAddress = $this->createOrderBillingAddress(); 46 | 47 | $this->assertSame('John', $billingAddress->first_name); 48 | $this->assertSame('Doe', $billingAddress->last_name); 49 | $this->assertSame('ACME corp.', $billingAddress->company); 50 | $this->assertSame('Example Street 10', $billingAddress->address_1); 51 | $this->assertSame('Test Address', $billingAddress->address_2); 52 | $this->assertSame('Los Angeles', $billingAddress->city); 53 | $this->assertSame('California', $billingAddress->state); 54 | $this->assertSame('00000', $billingAddress->postcode); 55 | $this->assertSame('USA', $billingAddress->country); 56 | $this->assertSame('john@doe.com', $billingAddress->email); 57 | $this->assertSame('00-00-000-000', $billingAddress->phone); 58 | } 59 | 60 | public function test_customer_billing_address_properties(): void 61 | { 62 | $billingAddress = $this->createCustomerBillingAddress(); 63 | 64 | $this->assertSame('John', $billingAddress->first_name); 65 | $this->assertSame('Doe', $billingAddress->last_name); 66 | $this->assertSame('ACME corp.', $billingAddress->company); 67 | $this->assertSame('Example Street 10', $billingAddress->address_1); 68 | $this->assertSame('Test Address', $billingAddress->address_2); 69 | $this->assertSame('Los Angeles', $billingAddress->city); 70 | $this->assertSame('California', $billingAddress->state); 71 | $this->assertSame('00000', $billingAddress->postcode); 72 | $this->assertSame('USA', $billingAddress->country); 73 | $this->assertSame('john@doe.com', $billingAddress->email); 74 | $this->assertSame('00-00-000-000', $billingAddress->phone); 75 | } 76 | 77 | public function test_to_array_method(): void 78 | { 79 | $billingAddress = $this->createOrderBillingAddress(); 80 | 81 | $array = [ 82 | 'first_name' => 'John', 83 | 'last_name' => 'Doe', 84 | 'company' => 'ACME corp.', 85 | 'address_1' => 'Example Street 10', 86 | 'address_2' => 'Test Address', 87 | 'city' => 'Los Angeles', 88 | 'state' => 'California', 89 | 'postcode' => '00000', 90 | 'country' => 'USA', 91 | 'email' => 'john@doe.com', 92 | 'phone' => '00-00-000-000', 93 | ]; 94 | 95 | $this->assertSame($array, $billingAddress->toArray()); 96 | } 97 | 98 | public function test_to_json_method(): void 99 | { 100 | $billingAddress = $this->createOrderBillingAddress(); 101 | 102 | $json = '{"first_name":"John","last_name":"Doe","company":"ACME corp.","address_1":"Example Street 10","address_2":"Test Address","city":"Los Angeles","state":"California","postcode":"00000","country":"USA","email":"john@doe.com","phone":"00-00-000-000"}'; 103 | 104 | $this->assertSame($json, $billingAddress->toJson()); 105 | } 106 | 107 | public function test_invalid_property(): void 108 | { 109 | $this->expectException(InvalidArgumentException::class); 110 | 111 | $billingAddress = $this->createOrderBillingAddress(); 112 | $billingAddress->unknown; // @phpstan-ignore-line 113 | } 114 | 115 | private function createOrderBillingAddress(): BillingAddress 116 | { 117 | /** @var Order */ 118 | $order = Order::factory()->create(); 119 | $order->createMeta(self::ORDER_META_FIELDS); 120 | 121 | return new BillingAddress($order); 122 | } 123 | 124 | private function createCustomerBillingAddress(): BillingAddress 125 | { 126 | /** @var Customer */ 127 | $customer = Customer::factory()->create(); 128 | $customer->createMeta(self::CUSTOMER_META_FIELDS); 129 | 130 | return new BillingAddress($customer); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /tests/Unit/Support/PaymentTest.php: -------------------------------------------------------------------------------- 1 | createPayment(); 17 | 18 | $this->assertSame('test', $payment->method); 19 | $this->assertSame('Test', $payment->method_title); 20 | $this->assertSame('tid-000', $payment->transaction_id); 21 | } 22 | 23 | public function test_to_array_method(): void 24 | { 25 | $payment = $this->createPayment(); 26 | $array = [ 27 | 'method' => 'test', 28 | 'method_title' => 'Test', 29 | 'transaction_id' => 'tid-000', 30 | ]; 31 | 32 | $this->assertSame($array, $payment->toArray()); 33 | } 34 | 35 | public function test_to_json_method(): void 36 | { 37 | $payment = $this->createPayment(); 38 | $json = '{"method":"test","method_title":"Test","transaction_id":"tid-000"}'; 39 | 40 | $this->assertSame($json, $payment->toJson()); 41 | } 42 | 43 | public function test_invalid_json(): void 44 | { 45 | $this->expectException(InvalidArgumentException::class); 46 | 47 | /** @var Order */ 48 | $order = Order::factory()->create(); 49 | $payment = new class($order) extends Payment 50 | { 51 | public function toArray(): array 52 | { 53 | return [ 54 | 'invalid' => fopen('php://input', 'r'), 55 | ]; 56 | } 57 | }; 58 | 59 | $payment->toJson(); 60 | } 61 | 62 | private function createPayment(): Payment 63 | { 64 | /** @var Order */ 65 | $order = Order::factory()->create(); 66 | $order->createMeta([ 67 | '_payment_method' => 'test', 68 | '_payment_method_title' => 'Test', 69 | '_transaction_id' => 'tid-000', 70 | ]); 71 | 72 | return new Payment($order); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Unit/Support/ShippingAddressTest.php: -------------------------------------------------------------------------------- 1 | 'John', 17 | '_shipping_last_name' => 'Doe', 18 | '_shipping_company' => 'ACME corp.', 19 | '_shipping_address_1' => 'Example Street 10', 20 | '_shipping_address_2' => 'Test Address', 21 | '_shipping_city' => 'Los Angeles', 22 | '_shipping_state' => 'California', 23 | '_shipping_postcode' => '00000', 24 | '_shipping_country' => 'USA', 25 | ]; 26 | 27 | private const CUSTOMER_META_FIELDS = [ 28 | 'shipping_first_name' => 'John', 29 | 'shipping_last_name' => 'Doe', 30 | 'shipping_company' => 'ACME corp.', 31 | 'shipping_address_1' => 'Example Street 10', 32 | 'shipping_address_2' => 'Test Address', 33 | 'shipping_city' => 'Los Angeles', 34 | 'shipping_state' => 'California', 35 | 'shipping_postcode' => '00000', 36 | 'shipping_country' => 'USA', 37 | ]; 38 | 39 | public function test_order_shipping_address_properties(): void 40 | { 41 | $shippingAddress = $this->createOrderShippingAddress(); 42 | 43 | $this->assertSame('John', $shippingAddress->first_name); 44 | $this->assertSame('Doe', $shippingAddress->last_name); 45 | $this->assertSame('ACME corp.', $shippingAddress->company); 46 | $this->assertSame('Example Street 10', $shippingAddress->address_1); 47 | $this->assertSame('Test Address', $shippingAddress->address_2); 48 | $this->assertSame('Los Angeles', $shippingAddress->city); 49 | $this->assertSame('California', $shippingAddress->state); 50 | $this->assertSame('00000', $shippingAddress->postcode); 51 | $this->assertSame('USA', $shippingAddress->country); 52 | } 53 | 54 | public function test_customer_shipping_address_properties(): void 55 | { 56 | $shippingAddress = $this->createCustomerShippingAddress(); 57 | 58 | $this->assertSame('John', $shippingAddress->first_name); 59 | $this->assertSame('Doe', $shippingAddress->last_name); 60 | $this->assertSame('ACME corp.', $shippingAddress->company); 61 | $this->assertSame('Example Street 10', $shippingAddress->address_1); 62 | $this->assertSame('Test Address', $shippingAddress->address_2); 63 | $this->assertSame('Los Angeles', $shippingAddress->city); 64 | $this->assertSame('California', $shippingAddress->state); 65 | $this->assertSame('00000', $shippingAddress->postcode); 66 | $this->assertSame('USA', $shippingAddress->country); 67 | } 68 | 69 | public function test_to_array_method(): void 70 | { 71 | $shippingAddress = $this->createOrderShippingAddress(); 72 | 73 | $array = [ 74 | 'first_name' => 'John', 75 | 'last_name' => 'Doe', 76 | 'company' => 'ACME corp.', 77 | 'address_1' => 'Example Street 10', 78 | 'address_2' => 'Test Address', 79 | 'city' => 'Los Angeles', 80 | 'state' => 'California', 81 | 'postcode' => '00000', 82 | 'country' => 'USA', 83 | ]; 84 | 85 | $this->assertSame($array, $shippingAddress->toArray()); 86 | } 87 | 88 | public function test_to_json_method(): void 89 | { 90 | $shippingAddress = $this->createOrderShippingAddress(); 91 | 92 | $json = '{"first_name":"John","last_name":"Doe","company":"ACME corp.","address_1":"Example Street 10","address_2":"Test Address","city":"Los Angeles","state":"California","postcode":"00000","country":"USA"}'; 93 | 94 | $this->assertSame($json, $shippingAddress->toJson()); 95 | } 96 | 97 | public function test_invalid_property(): void 98 | { 99 | $this->expectException(InvalidArgumentException::class); 100 | 101 | $shippingAddress = $this->createOrderShippingAddress(); 102 | $shippingAddress->unknown; // @phpstan-ignore-line 103 | } 104 | 105 | private function createOrderShippingAddress(): ShippingAddress 106 | { 107 | /** @var Order */ 108 | $order = Order::factory()->create(); 109 | $order->createMeta(self::ORDER_META_FIELDS); 110 | 111 | return new ShippingAddress($order); 112 | } 113 | 114 | private function createCustomerShippingAddress(): ShippingAddress 115 | { 116 | /** @var Customer */ 117 | $customer = Customer::factory()->create(); 118 | $customer->createMeta(self::CUSTOMER_META_FIELDS); 119 | 120 | return new ShippingAddress($customer); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tests/Unit/Traits/HasRelationsThroughMetaTest.php: -------------------------------------------------------------------------------- 1 | expectException(LogicException::class); 19 | 20 | $model = new class extends Model 21 | { 22 | /** @use HasRelationsThroughMeta */ 23 | use HasRelationsThroughMeta; 24 | 25 | /** 26 | * @return HasMany 27 | */ 28 | public function relatedObjects(): HasMany 29 | { 30 | return $this->hasManyThroughMeta(Model::class, '_meta_key', 'foreign_key', 'local_key'); 31 | } 32 | }; 33 | 34 | $model->relatedObjects; // @phpstan-ignore-line 35 | } 36 | 37 | public function test_invalid_meta(): void 38 | { 39 | $this->expectException(InvalidArgumentException::class); 40 | 41 | $model = new class extends Model 42 | { 43 | /** @use HasRelationsThroughMeta */ 44 | use HasRelationsThroughMeta; 45 | 46 | /** @var \Corcel\Model */ 47 | private $relatedModel; 48 | 49 | public function setRelatedModel(Model $relatedModel): void 50 | { 51 | $this->relatedModel = $relatedModel; 52 | } 53 | 54 | /** 55 | * @return HasMany 56 | */ 57 | public function relatedObjects(): HasMany 58 | { 59 | return $this->hasManyThroughMeta(get_class($this->relatedModel), '_meta_key', 'foreign_key', 'local_key'); 60 | } 61 | }; 62 | 63 | $model->setRelatedModel(new class extends Model 64 | { 65 | /** 66 | * @return HasMany<\Corcel\Model, Model> 67 | */ 68 | public function meta(): HasMany 69 | { 70 | return $this->hasMany(Model::class); 71 | } 72 | }); 73 | 74 | $model->relatedObjects; // @phpstan-ignore-line 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Unit/WooCommerceTest.php: -------------------------------------------------------------------------------- 1 | insert([ 16 | 'option_name' => 'woocommerce_currency', 17 | 'option_value' => 'EUR', 18 | ]); 19 | 20 | $this->assertSame('EUR', WooCommerce::currency()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/database/factories/CustomerFactory.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class CustomerFactory extends Factory 15 | { 16 | protected $model = Customer::class; 17 | 18 | /** 19 | * Define the model's default state. 20 | * 21 | * @return array 22 | */ 23 | public function definition() 24 | { 25 | $name = $this->faker->firstName; 26 | 27 | return [ 28 | 'user_login' => Str::lower(Str::ascii($name)), 29 | 'user_pass' => bcrypt('secret'), 30 | 'user_nicename' => $name, 31 | 'user_email' => $this->faker->email, 32 | 'user_url' => $this->faker->url, 33 | 'user_registered' => $this->faker->dateTime, 34 | 'user_activation_key' => Str::random(10), 35 | 'user_status' => 0, 36 | 'display_name' => $name, 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/database/factories/ItemFactory.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ItemFactory extends Factory 14 | { 15 | protected $model = Item::class; 16 | 17 | /** 18 | * Define the model's default state. 19 | * 20 | * @return array 21 | */ 22 | public function definition() 23 | { 24 | return [ 25 | 'order_item_name' => $this->faker->words(mt_rand(2, 4), true), 26 | 'order_item_type' => $this->faker->randomElement(['line_item', 'tax', 'coupon']), 27 | 'order_id' => $this->faker->numberBetween(1, 10000), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/database/factories/OrderFactory.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class OrderFactory extends Factory 16 | { 17 | protected $model = Order::class; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition() 25 | { 26 | $createdAt = DateTimeImmutable::createFromMutable($this->faker->dateTime('now', 'Europe/Warsaw')); 27 | $createdAtGMT = $createdAt->setTimezone(new DateTimeZone('UTC')); 28 | 29 | return [ 30 | 'post_author' => $this->faker->numberBetween(1, 100), 31 | 'post_date' => $createdAt->format('Y-m-d H:i:s'), 32 | 'post_date_gmt' => $createdAtGMT->format('Y-m-d H:i:s'), 33 | 'post_content' => '', 34 | 'post_title' => sprintf('Order – %s', $createdAt->format('F j, Y @ h:i A')), 35 | 'post_excerpt' => '', 36 | 'post_status' => 'wc-completed', 37 | 'comment_status' => 'closed', 38 | 'ping_status' => 'closed', 39 | 'post_password' => '', 40 | 'post_name' => '', 41 | 'to_ping' => '', 42 | 'pinged' => '', 43 | 'post_modified' => $createdAt->format('Y-m-d H:i:s'), 44 | 'post_modified_gmt' => $createdAtGMT->format('Y-m-d H:i:s'), 45 | 'post_content_filtered' => '', 46 | 'post_parent' => 0, 47 | 'guid' => 'http://woocommerce.example/?post_type=shop_order&p=1', 48 | 'menu_order' => 0, 49 | 'post_type' => 'shop_order', 50 | 'post_mime_type' => '', 51 | 'comment_count' => 0, 52 | ]; 53 | } 54 | 55 | /** 56 | * Applies pending state to model. 57 | * 58 | * @return Factory 59 | */ 60 | public function pending(): Factory 61 | { 62 | return $this->state(fn () => ['post_status' => 'wc-pending']); 63 | } 64 | 65 | /** 66 | * Applies cancelling state to model. 67 | * 68 | * @return Factory 69 | */ 70 | public function cancelled(): Factory 71 | { 72 | return $this->state(fn () => ['post_status' => 'wc-cancelled']); 73 | } 74 | 75 | /** 76 | * Applies refunded state to model. 77 | * 78 | * @return Factory 79 | */ 80 | public function refunded(): Factory 81 | { 82 | return $this->state(fn () => ['post_status' => 'wc-refunded']); 83 | } 84 | 85 | /** 86 | * Applies withMeta state to model. 87 | * 88 | * @return Factory 89 | */ 90 | public function withMeta(): Factory 91 | { 92 | return $this->afterCreating(function (Order $order) { 93 | $order->createMeta([ 94 | '_order_currency' => $this->faker->currencyCode, 95 | '_order_total' => $this->faker->randomFloat(2, 0, 200), 96 | '_order_tax' => $this->faker->randomFloat(2, 0, 200), 97 | '_date_completed' => $this->faker->dateTime->format('Y-m-d H:i:s'), 98 | '_billing_first_name' => $this->faker->firstName, 99 | '_billing_last_name' => $this->faker->lastName, 100 | '_billing_company' => $this->faker->optional()->company, 101 | '_billing_address_1' => $this->faker->streetAddress, 102 | '_billing_address_2' => $this->faker->optional()->secondaryAddress, 103 | '_billing_city' => $this->faker->city, 104 | '_billing_state' => $this->faker->state, 105 | '_billing_postcode' => $this->faker->postcode, 106 | '_billing_country' => $this->faker->country, 107 | '_billing_email' => $this->faker->email, 108 | '_billing_phone' => $this->faker->phoneNumber, 109 | ]); 110 | }); 111 | } 112 | 113 | /** 114 | * Applies withShipping state to model. 115 | * 116 | * @return Factory 117 | */ 118 | public function withShipping(): Factory 119 | { 120 | return $this->afterCreating(function (Order $order) { 121 | $order->createMeta([ 122 | '_order_shipping' => $this->faker->randomFloat(2, 0, 200), 123 | '_order_shipping_tax' => $this->faker->randomFloat(2, 0, 200), 124 | '_shipping_first_name' => $this->faker->firstName, 125 | '_shipping_last_name' => $this->faker->lastName, 126 | '_shipping_company' => $this->faker->optional()->company, 127 | '_shipping_address_1' => $this->faker->streetAddress, 128 | '_shipping_address_2' => $this->faker->optional()->secondaryAddress, 129 | '_shipping_city' => $this->faker->city, 130 | '_shipping_state' => $this->faker->state, 131 | '_shipping_postcode' => $this->faker->postcode, 132 | '_shipping_country' => $this->faker->country, 133 | ]); 134 | }); 135 | } 136 | 137 | /** 138 | * Applies paid state to model. 139 | * 140 | * @return Factory 141 | */ 142 | public function paid(): Factory 143 | { 144 | return $this->afterCreating(function (Order $order) { 145 | $paymentMethod = $this->faker->word; 146 | 147 | $order->createMeta([ 148 | '_date_paid' => $this->faker->dateTime->format('Y-m-d H:i:s'), 149 | '_payment_method' => $paymentMethod, 150 | '_payment_method_title' => ucfirst($paymentMethod), 151 | '_transaction_id' => $this->faker->asciify('*******'), 152 | ]); 153 | }); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /tests/database/factories/ProductFactory.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class ProductFactory extends Factory 17 | { 18 | protected $model = Product::class; 19 | 20 | /** 21 | * Define the model's default state. 22 | * 23 | * @return array 24 | */ 25 | public function definition() 26 | { 27 | $createdAt = DateTimeImmutable::createFromMutable($this->faker->dateTime('now', 'Europe/Warsaw')); 28 | $createdAtGMT = $createdAt->setTimezone(new DateTimeZone('UTC')); 29 | $name = $this->faker->sentence; 30 | 31 | return [ 32 | 'post_author' => $this->faker->numberBetween(1, 100), 33 | 'post_date' => $createdAt->format('Y-m-d H:i:s'), 34 | 'post_date_gmt' => $createdAtGMT->format('Y-m-d H:i:s'), 35 | 'post_content' => $this->faker->paragraphs(mt_rand(1, 5), true), 36 | 'post_title' => $name, 37 | 'post_excerpt' => $this->faker->paragraph, 38 | 'post_status' => 'publish', 39 | 'comment_status' => 'open', 40 | 'ping_status' => 'closed', 41 | 'post_password' => '', 42 | 'post_name' => Str::title($name), 43 | 'to_ping' => '', 44 | 'pinged' => '', 45 | 'post_modified' => $createdAt->format('Y-m-d H:i:s'), 46 | 'post_modified_gmt' => $createdAtGMT->format('Y-m-d H:i:s'), 47 | 'post_content_filtered' => '', 48 | 'post_parent' => 0, 49 | 'guid' => 'http://woocommerce.example/?post_type=product&p=1', 50 | 'menu_order' => 0, 51 | 'post_type' => 'product', 52 | 'post_mime_type' => '', 53 | 'comment_count' => 0, 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_options_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('option_id'); 20 | $table->string('option_name', 191)->default('')->index(); 21 | $table->longText('option_value'); 22 | $table->string('autoload', 20)->default('yes'); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('options'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_postmeta_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('meta_id'); 20 | $table->unsignedBigInteger('post_id')->default(0)->index(); 21 | $table->string('meta_key', 255)->nullable(); 22 | $table->longText('meta_value')->nullable(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('postmeta'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_posts_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('ID'); 20 | $table->unsignedBigInteger('post_author')->default(0)->index(); 21 | $table->datetime('post_date')->default('0000-00-00 00:00:00')->index(); 22 | $table->datetime('post_date_gmt')->default('0000-00-00 00:00:00'); 23 | $table->longText('post_content'); 24 | $table->text('post_title'); 25 | $table->text('post_excerpt'); 26 | $table->string('post_status', 20)->default('publish')->index(); 27 | $table->string('comment_status', 20)->default('open'); 28 | $table->string('ping_status', 20)->default('open'); 29 | $table->string('post_password', 255)->default(''); 30 | $table->string('post_name', 200)->default('')->index(); 31 | $table->text('to_ping'); 32 | $table->text('pinged'); 33 | $table->datetime('post_modified')->default('0000-00-00 00:00:00'); 34 | $table->datetime('post_modified_gmt')->default('0000-00-00 00:00:00'); 35 | $table->longText('post_content_filtered'); 36 | $table->unsignedBigInteger('post_parent')->default(0)->index(); 37 | $table->string('guid', 255)->default(''); 38 | $table->integer('menu_order')->default(0); 39 | $table->string('post_type', 20)->default('post'); 40 | $table->string('post_mime_type', 100)->default(''); 41 | $table->bigInteger('comment_count')->default(0); 42 | }); 43 | } 44 | 45 | /** 46 | * Reverse the migrations. 47 | * 48 | * @return void 49 | */ 50 | public function down() 51 | { 52 | Schema::dropIfExists('posts'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_term_relationships_table.php: -------------------------------------------------------------------------------- 1 | unsignedBigInteger('object_id')->default(0)->primary(); 20 | $table->unsignedBigInteger('term_taxonomy_id')->default(0)->index(); 21 | $table->integer('term_order'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('term_relationships'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_term_taxonomy_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('term_taxonomy_id'); 20 | $table->unsignedBigInteger('term_id')->default(0)->index(); 21 | $table->string('taxonomy', 32)->default('')->index(); 22 | $table->longText('description'); 23 | $table->unsignedBigInteger('parent_id')->default(0); 24 | $table->unsignedBigInteger('count')->default(0); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('term_taxonomy'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_terms_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('term_id'); 20 | $table->string('name', 200)->default('')->index(); 21 | $table->string('slug', 200)->default('')->index(); 22 | $table->bigInteger('term_group')->default(0); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('terms'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_usermeta_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('umeta_id'); 20 | $table->unsignedBigInteger('user_id')->default(0)->index(); 21 | $table->string('meta_key', 255)->nullable(); 22 | $table->longText('meta_value')->nullable(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('usermeta'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('ID'); 20 | $table->string('user_login', 60)->default('')->index(); 21 | $table->string('user_pass', 255)->default(''); 22 | $table->string('user_nicename', 50)->default('')->index(); 23 | $table->string('user_email', 100)->default('')->index(); 24 | $table->string('user_url', 100)->default(''); 25 | $table->datetime('user_registered'); 26 | $table->string('user_activation_key', 255)->default(''); 27 | $table->integer('user_status')->default(0); 28 | $table->string('display_name', 250)->default(''); 29 | }); 30 | } 31 | 32 | /** 33 | * Reverse the migrations. 34 | * 35 | * @return void 36 | */ 37 | public function down() 38 | { 39 | Schema::dropIfExists('users'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_woocommerce_attribute_taxonomies_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('attribute_id'); 20 | $table->string('attribute_name', 200)->index(); 21 | $table->string('attribute_label', 200)->nullable(); 22 | $table->string('attribute_type', 20); 23 | $table->string('attribute_orderby', 20); 24 | $table->tinyInteger('attribute_public')->default(1); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('woocommerce_attribute_taxonomies'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_woocommerce_order_itemmeta_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('meta_id'); 20 | $table->unsignedBigInteger('order_item_id')->index(); 21 | $table->string('meta_key', 255)->nullable()->index(); 22 | $table->longText('meta_value')->nullable(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('woocommerce_order_itemmeta'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/database/migrations/2020_01_01_000000_create_woocommerce_order_items_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('order_item_id'); 20 | $table->text('order_item_name'); 21 | $table->string('order_item_type', 200)->default(''); 22 | $table->unsignedBigInteger('order_id')->index(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('woocommerce_order_items'); 34 | } 35 | } 36 | --------------------------------------------------------------------------------