Answers.Body: <p>I don't know of a plugin that could help here.</p>
  <p>Here's what I could find in the Craft codebase:</p>
  <ul>
  <li><p>The position of a new entry in a structure section is controlled by 3 query params when clicking the &quot;Create&quot; button: <code>parentId</code>, <code>before</code> or <code>after</code>. These params are used to either set the parent with <code>$entry-&gt;setParentId(...)</code> or <code>Craft::$app-&gt;getStructures()-&gt;moveBefore(...)</code> or <code>...-&gt;moveAfter(...)</code>. See <code>EntriesController.php:128</code> and <code>EntriesController.php:186</code>.</p>
  <ul>
  <li>If you could manage to manipulate one of those query params you could control the position of the new entry.</li>
  </ul>
  </li>
  <li><p>Another way could be using events: There is the <code>EVENT_BEFORE_SAVE_ELEMENT</code> event which you could use to override the parent ID of the new entry that is about to be saved. You'd have to create a small module and register a new event handler that takes care of that.</p>
  </li>
  </ul>
  <p>(Anyways, Craft must know the exact parent entry of the level 1 entry. How would you decide that? Would you take the first level 1 entry in the structure?)</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-05-01T09:49:29.497Z
Answers.LastActivityDate: 2023-05-01T09:49:29.497Z
Answers.UserDisplayName: Johannes
Answers.Body: <p>In my opinion, the best (and simplest) approach to this problem is to use a <a href="https://craftcms.com/docs/4.x/extend/extending-system-components.html#custom-validation-rules" rel="nofollow noreferrer">custom validation rule</a> to prevent authors from creating (i.e. publishing) entries using the wrong entry type at the wrong level.</p>
  <p>Adding a custom validation rule is relatively straight-forward, but requires a (private) <a href="https://craftcms.com/docs/4.x/extend/plugin-guide.html#preparation" rel="nofollow noreferrer">plugin</a> (Craft 4.4+ only) or a custom Yii <a href="https://craftcms.com/docs/4.x/extend/module-guide.html#preparation" rel="nofollow noreferrer">module</a>. For this use case, a module will suffice and would be my recommendation – but assuming you're running Craft 4.3.5 or later, scaffolding either one is quite easy using the official <a href="https://craftcms.com/blog/craft-generator" rel="nofollow noreferrer">Craft Generator</a> package._</p>
  <p>Here's how you could add a custom validation rule that would prevent authors from creating <code>entryType2</code> entries at the root level of the structure, and <code>entryType1</code> entries below the root level (this should be added to your module or plugin's <code>init()</code> method:</p>
  <pre class="lang-php prettyprint-override"><code>use craft\elements\Entry;
  use craft\events\DefineRulesEvent;
  use yii\base\Event;
  
  Event::on(
      Entry::class,
      Entry::EVENT_DEFINE_RULES,
      function(DefineRulesEvent $event) {
          /** @var Entry $entry */
          $entry = $event-&gt;sender;
  
          if ($entry-&gt;getSection()-&gt;handle != 'structureSectionHandle') {
              return;
          }
  
          $event-&gt;rules[] = ['typeId', function () use ($entry) {
              if ($entry-&gt;getType()-&gt;handle === 'entryType2' &amp;&amp; $entry-&gt;level === 1) {
                  // Prevent entries with the entry type &quot;entryType2&quot; from being created at level 1 in the structure
                  $entry-&gt;addError('typeId', Craft::t('site', 'Entry type 2 entries must have a parent entry. Select a parent entry in the &quot;Parent&quot; field below.'));
              } else if ($entry-&gt;getType()-&gt;handle === 'entryType1' &amp;&amp; $entry-&gt;level &gt; 1) {
                  // Prevent entries with the entry type &quot;entryType1&quot; from being created as a child entry (i.e. below level 1 in the structure
                  $entry-&gt;addError('typeId', Craft::t('site', 'Entry type 1 entries cannot have a parent. Remove the parent entry in the &quot;Parent&quot; field below.'));
              }
          }, 'on' =&gt; Entry::SCENARIO_LIVE];
      }
  );
  </code></pre>
  <p>With the above validation rule, any <code>entryType2</code> entries without a parent, or <code>entryType1</code> entries <em>with</em> a parent, will fail validation and cannot be created as a live entry (they can be saved as drafts, though).</p>
  <p><a href="https://i.sstatic.net/Jb0Bk.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/Jb0Bk.png" alt="enter image description here" /></a></p>
  <p><strong>Note: If the validation error doesn't display, make sure your Craft install is upgraded to 4.4.9 or later.</strong></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-05-03T09:21:54.587Z
Answers.LastActivityDate: 2023-05-03T09:21:54.587Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I often use multiple Entry Types in a Structure so that the 'Landing page' and 'Entries' are in the same place in the admin for the site editor... e.g.</p>
  <ul>
  <li>L1 Landing Page (header image, introduction etc) = <em>entryType<strong>1</strong></em>
  <ul>
  <li>L2 Entry = <em>entryType<strong>2</strong></em></li>
  <li>L2 Entry = <em>entryType<strong>2</strong></em></li>
  <li>L2 Entry = <em>entryType<strong>2</strong></em></li>
  <li>L2 Entry = <em>entryType<strong>2</strong></em></li>
  <li>L2 Entry = <em>entryType<strong>2</strong></em></li>
  </ul>
  </li>
  </ul>
  <p>...etc...</p>
  <p>I then set entryType2 (regular entry) as default as this is likely the one most often used by the editor and helps prevent confusion.</p>
  <p>However, when an entry (entryType2) is saved it automatically defaults to Level 1 in the Structure and then needs to be manually 'dragged' into the Level 2 position.  Editors <strong>always</strong> forget this step and then wonder why their post is not showing on the front end.</p>
  <p>So... my question... Is there a better way to do this that I am missing?  Is there a plugin which could help?</p>
  <p>I think the ideal scenario would be an option in the CP when creating an Entry Type to choose which Level the Entry Type should be associated with.</p>
  <p>I hope that makes sense?</p>
  <p>Many thanks</p>
  
Tags: <control-panel><structure><entry-type>
Title: Automatically save an Entry Type to a specified Structure level
ThreadText: # Automatically save an Entry Type to a specified Structure level
  
  (Asked by: username_0 on 2023-04-29)
  
  I often use multiple Entry Types in a Structure so that the 'Landing page' and 'Entries' are in the same place in the admin for the site editor... e.g.
  
  * L1 Landing Page (header image, introduction etc) = *entryType**1***
    + L2 Entry = *entryType**2***
    + L2 Entry = *entryType**2***
    + L2 Entry = *entryType**2***
    + L2 Entry = *entryType**2***
    + L2 Entry = *entryType**2***
  
  ...etc...
  
  I then set entryType2 (regular entry) as default as this is likely the one most often used by the editor and helps prevent confusion.
  
  However, when an entry (entryType2) is saved it automatically defaults to Level 1 in the Structure and then needs to be manually 'dragged' into the Level 2 position. Editors **always** forget this step and then wonder why their post is not showing on the front end.
  
  So... my question... Is there a better way to do this that I am missing? Is there a plugin which could help?
  
  I think the ideal scenario would be an option in the CP when creating an Entry Type to choose which Level the Entry Type should be associated with.
  
  I hope that makes sense?
  
  Many thanks
  
  **Tags:** control-panel, structure, entry-type
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2023-05-01. Score: 1
  
  I don't know of a plugin that could help here.
  
  Here's what I could find in the Craft codebase:
  
  * The position of a new entry in a structure section is controlled by 3 query params when clicking the "Create" button: `parentId`, `before` or `after`. These params are used to either set the parent with `$entry->setParentId(...)` or `Craft::$app->getStructures()->moveBefore(...)` or `...->moveAfter(...)`. See `EntriesController.php:128` and `EntriesController.php:186`.
  
    + If you could manage to manipulate one of those query params you could control the position of the new entry.
  * Another way could be using events: There is the `EVENT_BEFORE_SAVE_ELEMENT` event which you could use to override the parent ID of the new entry that is about to be saved. You'd have to create a small module and register a new event handler that takes care of that.
  
  (Anyways, Craft must know the exact parent entry of the level 1 entry. How would you decide that? Would you take the first level 1 entry in the structure?)
  
  
  ---
  
  ### Answer by username_2 on 2023-05-03. Score: 1
  
  In my opinion, the best (and simplest) approach to this problem is to use a [custom validation rule](https://craftcms.com/docs/4.x/extend/extending-system-components.html#custom-validation-rules) to prevent authors from creating (i.e. publishing) entries using the wrong entry type at the wrong level.
  
  Adding a custom validation rule is relatively straight-forward, but requires a (private) [plugin](https://craftcms.com/docs/4.x/extend/plugin-guide.html#preparation) (Craft 4.4+ only) or a custom Yii [module](https://craftcms.com/docs/4.x/extend/module-guide.html#preparation). For this use case, a module will suffice and would be my recommendation – but assuming you're running Craft 4.3.5 or later, scaffolding either one is quite easy using the official [Craft Generator](https://craftcms.com/blog/craft-generator) package.\_
  
  Here's how you could add a custom validation rule that would prevent authors from creating `entryType2` entries at the root level of the structure, and `entryType1` entries below the root level (this should be added to your module or plugin's `init()` method:
  
  ```php
  use craft\elements\Entry;
  use craft\events\DefineRulesEvent;
  use yii\base\Event;
  
  Event::on(
      Entry::class,
      Entry::EVENT_DEFINE_RULES,
      function(DefineRulesEvent $event) {
          /** @var Entry $entry */
          $entry = $event->sender;
  
          if ($entry->getSection()->handle != 'structureSectionHandle') {
              return;
          }
  
          $event->rules[] = ['typeId', function () use ($entry) {
              if ($entry->getType()->handle === 'entryType2' && $entry->level === 1) {
                  // Prevent entries with the entry type "entryType2" from being created at level 1 in the structure
                  $entry->addError('typeId', Craft::t('site', 'Entry type 2 entries must have a parent entry. Select a parent entry in the "Parent" field below.'));
              } else if ($entry->getType()->handle === 'entryType1' && $entry->level > 1) {
                  // Prevent entries with the entry type "entryType1" from being created as a child entry (i.e. below level 1 in the structure
                  $entry->addError('typeId', Craft::t('site', 'Entry type 1 entries cannot have a parent. Remove the parent entry in the "Parent" field below.'));
              }
          }, 'on' => Entry::SCENARIO_LIVE];
      }
  );
  
  ```
  
  With the above validation rule, any `entryType2` entries without a parent, or `entryType1` entries *with* a parent, will fail validation and cannot be created as a live entry (they can be saved as drafts, though).
  
  [![enter image description here](https://i.sstatic.net/Jb0Bk.png)](https://i.sstatic.net/Jb0Bk.png)
  
  **Note: If the validation error doesn't display, make sure your Craft install is upgraded to 4.4.9 or later.**
  
  
  ---
  
  