Answers.Body: <p>I ended up using a ProductQuery <a href="https://craftcms.com/docs/4.x/extend/behaviors.html" rel="nofollow noreferrer">Behavior</a>. This is quite handy as the same query is used in different pages. My final solution:</p>
  <p><strong>Twig</strong></p>
  <pre><code>{% set tempQuery = craft.products()
      .type('product')
      .whereParentProduct()
  %}
  </code></pre>
  <p><strong>Behavior Class</strong></p>
  <pre><code>&lt;?php
  
  namespace module\base\behaviors;
  
  use craft\commerce\elements\db\ProductQuery;
  use yii\base\Behavior;
  
  /**
   * @property ProductQuery $owner
   */
  class ParentProductQuery extends Behavior
  {
      public function whereParentProduct()
      {
          return $this-&gt;owner-&gt;where([
              'or',
              ['field_onlyShowMainProduct_njwytbyx' =&gt; [0, null]],
              ['and',
                  ['field_onlyShowMainProduct_njwytbyx' =&gt; 1],
                  ['field_isMainProduct_ftgnylbr' =&gt; 1]
              ]
          ]);
      }
  }
  </code></pre>
  <p><strong>Adding the Behavior using an Event</strong></p>
  <pre><code>Event::on(
      ProductQuery::class,
      ProductQuery::EVENT_DEFINE_BEHAVIORS,
      function (DefineBehaviorsEvent $event) {
          $event-&gt;behaviors['parentProductQuery'] = ParentProductQuery::class;
      }
  );
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-10-26T14:02:06.040Z
Answers.LastActivityDate: 2023-10-26T14:02:06.040Z
Answers.UserDisplayName: bartpulse
Answers.Body: <p>Ordinarily I'm a fan of being as lean and efficient as possible, but it strikes me that this <code>where</code> clause makes your Twig quite hard to read - as you say, getting the all the square brackets and quote marks and commas in the right place is fiddly and error-prone. You might want to consider two alternative approaches, which would be less efficient but more readable:</p>
  <pre class="lang-twig prettyprint-override"><code>{# ==================================
  Approach 1: as you were, make a complicated-
  looking query to get only what we need.
  (I haven't tested this, sorry in advance if it's not quite right!)
  ===================================== #}
  
  {% set tempQuery = craft.products()
      .type('product')
      .where([
          'or', [
              ['field_onlyShowMainProduct_njwytbyx', [0, null]],
              ['and', [
                  ['field_onlyShowMainProduct_njwytbyx', 1],
                  ['field_isMainProduct_ftgnylbr', 1]
              ]]
          ]
      ])
      .all()
  %}
  
  {# ==================================
  Approach 2: make a pair of simpler
  separate queries and then merge them
  ===================================== #}
  
  {% set firstSet = craft.products()
      .type('product')
      .onlyShowMainProduct(false)
      .all()
  %}
  {% set secondSet = craft.products()
      .type('product')
      .onlyShowMainProduct(true)
      .isMainProduct(true)
      .all()
  %}
  {% set mergedResults = firstSet|merge(secondSet) %}
  
  {# ==================================
  Approach 3: make a single large query
  and then filter the results
  ===================================== #}
  
  {% set allProducts = craft.products().type('product').all() %}
  {% set results = allProducts|filter(product =&gt;
      not product.onlyShowMainProduct or
      (product.onlyShowMainProduct and product.isMainProduct)
  ) %}
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-10-26T13:55:15.777Z
Answers.Text: Thanks for your response. Glad to hear I'm not the only one who thinks this notation is convoluted. 
  
  I can't use the combining solution as I need to pass the query to a pager function. I'm posting the solution I ended up using below.
Answers.UserDisplayName: bartpulse
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-10-26T17:11:47.490Z
Answers.Text: Ok cool. Curious why you can't use Craft's built-in pagination functionality? You don't need to call `.all()` on each query - you could use `.ids()` instead to build up an array of ids before then creating the final query for those ids, which then gets paginated.
Answers.UserDisplayName: James Smith
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-10-25T16:53:34.430Z
Answers.LastActivityDate: 2023-10-25T18:36:21.600Z
Answers.LastEditDate: 2023-10-25T18:36:21.600Z
Answers.UserDisplayName: James Smith
Body: <p>I have troubles understanding the nested twig query syntax. It works fine in PHP code but somehow when using twig I never seem to get it right.</p>
  <p>This is my raw, dirty query which I want to clean up a bit:</p>
  <pre><code>{% set tempQuery   = craft.products()
      .type('product')
      .where('(field_onlyShowMainProduct_njwytbyx = 0 OR field_onlyShowMainProduct_njwytbyx IS 
      NULL) OR (field_onlyShowMainProduct_njwytbyx = 1 AND 
      field_f_product_isMainProduct_ftgnylbr = 1)')
  %}
  </code></pre>
  <p>I managed to get it cleaner in PHP:</p>
  <pre><code>Product::find()
      -&gt;type('product')
      -&gt;where([
              'or',
                  ['field_onlyShowMainProduct_njwytbyx' =&gt; [0, null]],
                  ['and',
                      ['field_onlyShowMainProduct_njwytbyx' =&gt; 1],
                      ['field_isMainProduct_ftgnylbr' =&gt; 1]
                  ]
              ])
  </code></pre>
  <p>But in twig I can't get it to work. I just get error after error. Feels like i'm missing some basic understanding of nesting and twig formatting arrays. When to use &quot;,&quot; or &quot;:&quot;, &quot;{&quot; or &quot;[&quot;. The examples in the Craft docs are also very simple queries. Any ideas? Suggestions? Fixes?</p>
  <pre><code>{% set tempQuery = craft.products()
      .type('product')
      .where({
          or: [
              { 'field_onlyShowMainProduct_njwytbyx': [0, null] },
              { and: [
                  { 'field_onlyShowMainProduct_njwytbyx': 1 },
                  { 'field__isMainProduct_ftgnylbr': 1 }
              ]}
          ]
      })
  %}
  </code></pre>
  
Tags: <craft3><query>
Title: Cleaning up twig query with nested and/or where
ThreadText: # Cleaning up twig query with nested and/or where
  
  (Asked by: username_0 on 2023-10-25)
  
  I have troubles understanding the nested twig query syntax. It works fine in PHP code but somehow when using twig I never seem to get it right.
  
  This is my raw, dirty query which I want to clean up a bit:
  
  ```
  {% set tempQuery   = craft.products()
      .type('product')
      .where('(field_onlyShowMainProduct_njwytbyx = 0 OR field_onlyShowMainProduct_njwytbyx IS 
      NULL) OR (field_onlyShowMainProduct_njwytbyx = 1 AND 
      field_f_product_isMainProduct_ftgnylbr = 1)')
  %}
  
  ```
  
  I managed to get it cleaner in PHP:
  
  ```
  Product::find()
      ->type('product')
      ->where([
              'or',
                  ['field_onlyShowMainProduct_njwytbyx' => [0, null]],
                  ['and',
                      ['field_onlyShowMainProduct_njwytbyx' => 1],
                      ['field_isMainProduct_ftgnylbr' => 1]
                  ]
              ])
  
  ```
  
  But in twig I can't get it to work. I just get error after error. Feels like i'm missing some basic understanding of nesting and twig formatting arrays. When to use "," or ":", "{" or "[". The examples in the Craft docs are also very simple queries. Any ideas? Suggestions? Fixes?
  
  ```
  {% set tempQuery = craft.products()
      .type('product')
      .where({
          or: [
              { 'field_onlyShowMainProduct_njwytbyx': [0, null] },
              { and: [
                  { 'field_onlyShowMainProduct_njwytbyx': 1 },
                  { 'field__isMainProduct_ftgnylbr': 1 }
              ]}
          ]
      })
  %}
  
  ```
  
  **Tags:** craft3, query
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2023-10-26. Score: 2
  
  I ended up using a ProductQuery [Behavior](https://craftcms.com/docs/4.x/extend/behaviors.html). This is quite handy as the same query is used in different pages. My final solution:
  
  **Twig**
  
  ```
  {% set tempQuery = craft.products()
      .type('product')
      .whereParentProduct()
  %}
  
  ```
  
  **Behavior Class**
  
  ```
  <?php
  
  namespace module\base\behaviors;
  
  use craft\commerce\elements\db\ProductQuery;
  use yii\base\Behavior;
  
  /**
   * @property ProductQuery $owner
   */
  class ParentProductQuery extends Behavior
  {
      public function whereParentProduct()
      {
          return $this->owner->where([
              'or',
              ['field_onlyShowMainProduct_njwytbyx' => [0, null]],
              ['and',
                  ['field_onlyShowMainProduct_njwytbyx' => 1],
                  ['field_isMainProduct_ftgnylbr' => 1]
              ]
          ]);
      }
  }
  
  ```
  
  **Adding the Behavior using an Event**
  
  ```
  Event::on(
      ProductQuery::class,
      ProductQuery::EVENT_DEFINE_BEHAVIORS,
      function (DefineBehaviorsEvent $event) {
          $event->behaviors['parentProductQuery'] = ParentProductQuery::class;
      }
  );
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2023-10-25. Score: 1
  
  Ordinarily I'm a fan of being as lean and efficient as possible, but it strikes me that this `where` clause makes your Twig quite hard to read - as you say, getting the all the square brackets and quote marks and commas in the right place is fiddly and error-prone. You might want to consider two alternative approaches, which would be less efficient but more readable:
  
  ```twig
  {# ==================================
  Approach 1: as you were, make a complicated-
  looking query to get only what we need.
  (I haven't tested this, sorry in advance if it's not quite right!)
  ===================================== #}
  
  {% set tempQuery = craft.products()
      .type('product')
      .where([
          'or', [
              ['field_onlyShowMainProduct_njwytbyx', [0, null]],
              ['and', [
                  ['field_onlyShowMainProduct_njwytbyx', 1],
                  ['field_isMainProduct_ftgnylbr', 1]
              ]]
          ]
      ])
      .all()
  %}
  
  {# ==================================
  Approach 2: make a pair of simpler
  separate queries and then merge them
  ===================================== #}
  
  {% set firstSet = craft.products()
      .type('product')
      .onlyShowMainProduct(false)
      .all()
  %}
  {% set secondSet = craft.products()
      .type('product')
      .onlyShowMainProduct(true)
      .isMainProduct(true)
      .all()
  %}
  {% set mergedResults = firstSet|merge(secondSet) %}
  
  {# ==================================
  Approach 3: make a single large query
  and then filter the results
  ===================================== #}
  
  {% set allProducts = craft.products().type('product').all() %}
  {% set results = allProducts|filter(product =>
      not product.onlyShowMainProduct or
      (product.onlyShowMainProduct and product.isMainProduct)
  ) %}
  
  ```
  
  > **Comments:**
  > **username_0**: Thanks for your response. Glad to hear I'm not the only one who thinks this notation is convoluted.   I can't use the combining solution as I need to pass the query to a pager function. I'm posting the solution I ended up using below.  
  > **username_1**: Ok cool. Curious why you can't use Craft's built-in pagination functionality? You don't need to call `.all()` on each query - you could use `.ids()` instead to build up an array of ids before then creating the final query for those ids, which then gets paginated.  
  
  ---
  
  