Skip to content

Commit

Permalink
Merge pull request #3233 from craftcms/feature/product-query-shipping…
Browse files Browse the repository at this point in the history
…-and-tax-category

Fixes #3219 Add shipping and tax category querying to products
  • Loading branch information
lukeholder authored Aug 1, 2023
2 parents 7578d10 + bab1b7c commit a53ddaa
Show file tree
Hide file tree
Showing 6 changed files with 505 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@

## 4.3.0 - Unreleased

- It’s now possible to query products by shipping category and tax category. ([#3219](https://github.com/craftcms/commerce/issues/3219))
- It’s now possible to modify the purchasables shown in the add line item table on the Edit Order page. ([#3194](https://github.com/craftcms/commerce/issues/3194))
- Added `craft\commerce\events\ModifyPurchasablesQueryEvent`.
- Added `craft\commerce\controllers\OrdersController::EVENT_MODIFY_PURCHASABLES_QUERY`.
- Guest customers registering during checkout now have their addresses saved to their account.
- Deprecated `craft\commerce\elements\Order::setEmail()`. `Order::setCustomer()` should be used instead.
- Added `craft\commerce\elements\db\ProductQuery::$shippingCategoryId`.
- Added `craft\commerce\elements\db\ProductQuery::$taxCategoryId`.
- Added `craft\commerce\elements\db\ProductQuery::shippingCategory()`.
- Added `craft\commerce\elements\db\ProductQuery::shippingCategoryId()`.
- Added `craft\commerce\elements\db\ProductQuery::taxCategory()`.
- Added `craft\commerce\elements\db\ProductQuery::taxCategoryId()`.

## Unreleased

Expand Down
206 changes: 206 additions & 0 deletions src/elements/db/ProductQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use craft\commerce\elements\Product;
use craft\commerce\elements\Variant;
use craft\commerce\models\ProductType;
use craft\commerce\models\ShippingCategory;
use craft\commerce\models\TaxCategory;
use craft\commerce\Plugin;
use craft\db\Query;
use craft\db\QueryAbortedException;
Expand All @@ -20,6 +22,7 @@
use craft\helpers\Db;
use DateTime;
use yii\db\Connection;
use yii\db\Expression;

/**
* ProductQuery represents a SELECT SQL statement for products in a way that is independent of DBMS.
Expand Down Expand Up @@ -104,6 +107,16 @@ class ProductQuery extends ElementQuery
*/
public mixed $typeId = null;

/**
* @var mixed The shipping category ID(s) that the resulting products must have.
*/
public mixed $shippingCategoryId = null;

/**
* @var mixed The tax category ID(s) that the resulting products must have.
*/
public mixed $taxCategoryId = null;

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -152,6 +165,9 @@ public function __set($name, $value)
case 'defaultSku':
$this->defaultSku($value);
break;
case 'shippingCategory':
$this->shippingCategory($value);
break;
default:
parent::__set($name, $value);
}
Expand Down Expand Up @@ -432,6 +448,176 @@ public function type(mixed $value): ProductQuery
return $this;
}

/**
* Narrows the query results based on the products’ shipping categories, per the shipping categories’ IDs.
*
* Possible values include:
*
* | Value | Fetches {elements}…
* | - | -
* | `1` | of a shipping category with an ID of 1.
* | `'not 1'` | not of a shipping category with an ID of 1.
* | `[1, 2]` | of a shipping category with an ID of 1 or 2.
* | `['not', 1, 2]` | not of a shipping category with an ID of 1 or 2.
*
* ---
*
* ```twig
* {# Fetch {elements} of the shipping category with an ID of 1 #}
* {% set {elements-var} = {twig-method}
* .shippingCategoryId(1)
* .all() %}
* ```
*
* ```php
* // Fetch {elements} of the shipping category with an ID of 1
* ${elements-var} = {php-method}
* ->shippingCategoryId(1)
* ->all();
* ```
*
* @param mixed $value The property value
* @return static self reference
*/
public function shippingCategoryId(mixed $value): ProductQuery
{
$this->shippingCategoryId = $value;
return $this;
}

/**
* Narrows the query results based on the products’ shipping category.
*
* Possible values include:
*
* | Value | Fetches {elements}…
* | - | -
* | `'foo'` | of a shipping category with a handle of `foo`.
* | `'not foo'` | not of a shipping category with a handle of `foo`.
* | `['foo', 'bar']` | of a shipping category with a handle of `foo` or `bar`.
* | `['not', 'foo', 'bar']` | not of a shipping category with a handle of `foo` or `bar`.
* | an [[ShippingCategory|ShippingCategory]] object | of a shipping category represented by the object.
*
* ---
*
* ```twig
* {# Fetch {elements} with a Foo shipping category #}
* {% set {elements-var} = {twig-method}
* .shippingCategory('foo')
* .all() %}
* ```
*
* ```php
* // Fetch {elements} with a Foo shipping category
* ${elements-var} = {php-method}
* ->shippingCategory('foo')
* ->all();
* ```
*
* @param ShippingCategory|string|null|array<string> $value The property value
* @return static self reference
*/
public function shippingCategory(mixed $value): ProductQuery
{
if ($value instanceof ShippingCategory) {
$this->shippingCategoryId = [$value->id];
} elseif ($value !== null) {
$this->shippingCategoryId = (new Query())
->from(['shippingcategories' => Table::SHIPPINGCATEGORIES])
->where(['shippingcategories.id' => new Expression('[[commerce_products.shippingCategoryId]]')])
->andWhere(Db::parseParam('handle', $value));
} else {
$this->shippingCategoryId = null;
}

return $this;
}

/**
* Narrows the query results based on the products’ tax categories, per the tax categories’ IDs.
*
* Possible values include:
*
* | Value | Fetches {elements}…
* | - | -
* | `1` | of a tax category with an ID of 1.
* | `'not 1'` | not of a tax category with an ID of 1.
* | `[1, 2]` | of a tax category with an ID of 1 or 2.
* | `['not', 1, 2]` | not of a tax category with an ID of 1 or 2.
*
* ---
*
* ```twig
* {# Fetch {elements} of the tax category with an ID of 1 #}
* {% set {elements-var} = {twig-method}
* .taxCategoryId(1)
* .all() %}
* ```
*
* ```php
* // Fetch {elements} of the tax category with an ID of 1
* ${elements-var} = {php-method}
* ->taxCategoryId(1)
* ->all();
* ```
*
* @param mixed $value The property value
* @return static self reference
*/
public function taxCategoryId(mixed $value): ProductQuery
{
$this->taxCategoryId = $value;
return $this;
}

/**
* Narrows the query results based on the products’ tax category.
*
* Possible values include:
*
* | Value | Fetches {elements}…
* | - | -
* | `'foo'` | of a tax category with a handle of `foo`.
* | `'not foo'` | not of a tax category with a handle of `foo`.
* | `['foo', 'bar']` | of a tax category with a handle of `foo` or `bar`.
* | `['not', 'foo', 'bar']` | not of a tax category with a handle of `foo` or `bar`.
* | an [[ShippingCategory|ShippingCategory]] object | of a tax category represented by the object.
*
* ---
*
* ```twig
* {# Fetch {elements} with a Foo tax category #}
* {% set {elements-var} = {twig-method}
* .taxCategory('foo')
* .all() %}
* ```
*
* ```php
* // Fetch {elements} with a Foo tax category
* ${elements-var} = {php-method}
* ->taxCategory('foo')
* ->all();
* ```
*
* @param TaxCategory|string|null|array<string> $value The property value
* @return static self reference
*/
public function taxCategory(mixed $value): ProductQuery
{
if ($value instanceof TaxCategory) {
$this->taxCategoryId = [$value->id];
} elseif ($value !== null) {
$this->taxCategoryId = (new Query())
->from(['taxcategories' => Table::TAXCATEGORIES])
->where(['taxcategories.id' => new Expression('[[commerce_products.taxCategoryId]]')])
->andWhere(Db::parseParam('handle', $value));
} else {
$this->taxCategoryId = null;
}

return $this;
}

/**
* Narrows the query results to only products that were posted before a certain date.
*
Expand Down Expand Up @@ -786,6 +972,26 @@ protected function beforePrepare(): bool
$this->subQuery->andWhere(['commerce_products.typeId' => $this->typeId]);
}

if (isset($this->shippingCategoryId)) {
if ($this->shippingCategoryId instanceof Query) {
$shippingCategoryWhere = ['exists', $this->shippingCategoryId];
} else {
$shippingCategoryWhere = Db::parseParam('commerce_products.shippingCategoryId', $this->shippingCategoryId);
}

$this->subQuery->andWhere($shippingCategoryWhere);
}

if (isset($this->taxCategoryId)) {
if ($this->taxCategoryId instanceof Query) {
$taxCategoryWhere = ['exists', $this->taxCategoryId];
} else {
$taxCategoryWhere = Db::parseParam('commerce_products.taxCategoryId', $this->taxCategoryId);
}

$this->subQuery->andWhere($taxCategoryWhere);
}

if (isset($this->defaultPrice)) {
$this->subQuery->andWhere(Db::parseParam('commerce_products.defaultPrice', $this->defaultPrice));
}
Expand Down
8 changes: 7 additions & 1 deletion tests/fixtures/ProductTypeFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,11 @@ class ProductTypeFixture extends ActiveFixture
/**
* @inheritdoc
*/
public $depends = [ProductTypeSitesFixture::class];
public $depends = [
ShippingCategoryFixture::class,
ProductTypesShippingCategoriesFixture::class,
TaxCategoryFixture::class,
ProductTypesTaxCategoriesFixture::class,
ProductTypeSitesFixture::class,
];
}
10 changes: 5 additions & 5 deletions tests/fixtures/data/shipping-category.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
return [
[
'id' => 101,
'name' => 'General 1',
'handle' => 'general_1',
'description' => 'this is the default shipping category',
'default' => '1',
'uid' => 'xx-xx-xx',
'name' => 'Another Shipping Category',
'handle' => 'anotherShippingCategory',
'description' => 'this is another shipping category',
'default' => 0,
'uid' => 'ship-category-1001---------------uid',
],
];
8 changes: 4 additions & 4 deletions tests/fixtures/data/tax-category.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
return [
[
'id' => 101,
'name' => 'General 1',
'handle' => 'general_1',
'description' => 'this is the default tax category',
'name' => 'Another Tax Category',
'handle' => 'anotherTaxCategory',
'description' => 'this is another tax category',
'default' => '1',
'uid' => 'xx-xx-xx',
'uid' => 'tax--category-1001---------------uid',
],
];
Loading

0 comments on commit a53ddaa

Please sign in to comment.