diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
new file mode 100644
index 0000000..0bd18f9
--- /dev/null
+++ b/.github/workflows/CI.yaml
@@ -0,0 +1,100 @@
+name: Tests
+
+# Run this workflow every time a new commit pushed to your repository
+on:
+  push:
+    paths-ignore:
+      - '**/*.md'
+  pull_request:
+    paths-ignore:
+      - '**/*.md'
+
+jobs:
+  tests:
+    runs-on: ${{ matrix.operating-system }}
+    if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository)
+
+    strategy:
+      fail-fast: false
+      matrix:
+        operating-system: [ubuntu-20.04]
+        php-versions: ['7.4', '8.0', '8.1']
+        dependencies: ['no', 'low', 'beta']
+        exclude:
+          - operating-system: ubuntu-20.04
+            php-versions: '8.1'
+            dependencies: 'low'
+
+    name: PHP ${{ matrix.php-versions }} - ${{ matrix.dependencies }}
+
+    env:
+      COMPOSER_NO_INTERACTION: 1
+      extensions: curl json libxml dom
+      key: cache-v1 # can be any string, change to clear the extension cache.
+
+    steps:
+
+      # Checks out a copy of your repository on the ubuntu machine
+      - name: Checkout code
+        uses: actions/checkout@v2
+
+      - name: Setup cache environment
+        id: extcache
+        uses: shivammathur/cache-extensions@v1
+        with:
+          php-version: ${{ matrix.php-versions }}
+          extensions: ${{ env.extensions }}
+          key: ${{ env.key }}
+
+      - name: Cache PHP Extensions
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.extcache.outputs.dir }}
+          key: ${{ steps.extcache.outputs.key }}
+          restore-keys: ${{ steps.extcache.outputs.key }}
+
+      - name: Cache Composer Dependencies
+        uses: actions/cache@v1
+        with:
+          path: ~/.composer/cache/files
+          key: dependencies-composer-${{ hashFiles('composer.json') }}
+
+      - name: Fix beta
+        if: ${{ matrix.dependencies == 'beta' }}
+        run: perl -pi -e 's/^}$/,"minimum-stability":"beta"}/' composer.json
+
+      - name: Setup PHP Action
+        uses: shivammathur/setup-php@2.8.0
+        with:
+          php-version: ${{ matrix.php-versions }}
+          extensions: ${{ env.extensions }}
+          coverage: xdebug
+          tools: pecl, composer
+      
+      - name: PHP Show modules
+        run: php -m
+
+      - name: Get composer cache directory
+        id: composer-cache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Cache dependencies
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composer-cache.outputs.dir }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: ${{ runner.os }}-composer-
+
+      - name: Install Composer dependencies
+        if: ${{ matrix.dependencies != 'low' }}
+        run: composer update --no-interaction
+
+      - name: Install Composer dependencies
+        if: ${{ matrix.dependencies == 'low' }}
+        run: composer update -vvv --prefer-lowest --prefer-stable --no-interaction
+
+      - name: Validate files
+        run: composer validate-files
+      
+      - name: Run tests
+        run: composer run-tests
\ No newline at end of file
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
deleted file mode 100644
index 438e5dd..0000000
--- a/.github/workflows/run-tests.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Unit Tests
-
-on:
-  push:
-    branches:
-    - master
-  pull_request:
-    branches:
-    - "*"
-  schedule:
-  - cron: '0 0 * * *'
-
-jobs:
-  php-tests:
-    runs-on: ubuntu-latest
-    timeout-minutes: 15
-    env:
-      COMPOSER_NO_INTERACTION: 1
-
-    strategy:
-      fail-fast: false
-      matrix:
-        php: [8.1, 8.0, 7.4, 7.3, 7.2]
-
-    name: P${{ matrix.php }}
-
-    steps:
-    - name: Checkout code
-      uses: actions/checkout@v2
-
-    - name: Setup PHP
-      uses: shivammathur/setup-php@v2
-      with:
-        php-version: ${{ matrix.php }}
-        coverage: none
-        tools: composer:v2
-
-    - name: Install dependencies
-      run: |
-        composer install -o --quiet
-
-    - name: Execute Unit Tests
-      run: composer test
diff --git a/.gitignore b/.gitignore
index 6bef520..24a2013 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
 composer.phar
 composer.lock
 .DS_Store
+.php-cs-fixer.cache
 .phpunit.result.cache
diff --git a/README.markdown b/README.markdown
index 7e7341e..67cd0bd 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,11 +1,15 @@
-[![Build Status](https://travis-ci.org/lazychaser/laravel-nestedset.svg?branch=master)](https://travis-ci.org/lazychaser/laravel-nestedset)
-[![Total Downloads](https://poser.pugx.org/kalnoy/nestedset/downloads.svg)](https://packagist.org/packages/kalnoy/nestedset)
-[![Latest Stable Version](https://poser.pugx.org/kalnoy/nestedset/v/stable.svg)](https://packagist.org/packages/kalnoy/nestedset)
-[![Latest Unstable Version](https://poser.pugx.org/kalnoy/nestedset/v/unstable.svg)](https://packagist.org/packages/kalnoy/nestedset)
-[![License](https://poser.pugx.org/kalnoy/nestedset/license.svg)](https://packagist.org/packages/kalnoy/nestedset)
+[![Total Downloads](https://poser.pugx.org/lychee-org/nestedset/downloads.svg)](https://packagist.org/packages/lychee-org/nestedset)
+[![Latest Stable Version](https://poser.pugx.org/lychee-org/nestedset/v/stable.svg)](https://packagist.org/packages/lychee-org/nestedset)
+[![Latest Unstable Version](https://poser.pugx.org/lychee-org/nestedset/v/unstable.svg)](https://packagist.org/packages/lychee-org/nestedset)
+[![License](https://poser.pugx.org/lychee-org/nestedset/license.svg)](https://packagist.org/packages/lychee-org/nestedset)
 
 This is a Laravel 4-8 package for working with trees in relational databases.
 
+It is a fork of [lazychaser/laravel-nestedset](https://github.com/lazychaser/laravel-nestedset) and contains general patches which are required for using the library with [Lychee](https://github.com/LycheeOrg/Lychee). Note that the patches are **not** specific for Lychee, but a generally useful. Inter alia:
+
+ * Routines respect a foreign key constraint on the parent-child-relation by taking care that changes to the tree are applied in the correct order.
+ * The code does not fail if the model which uses `NoteTrait` does not directly extend `Model` but indirectly inherits `Model` via another parent class.
+
 *   **Laravel 8.0** is supported since v6
 *   **Laravel 5.7, 5.8, 6.0, 7.0** is supported since v5
 *   **Laravel 5.5, 5.6** is supported since v4.3
@@ -13,10 +17,6 @@ This is a Laravel 4-8 package for working with trees in relational databases.
 *   **Laravel 5.1** is supported in v3
 *   **Laravel 4** is supported in v2
 
-Although this project is completely free for use, I appreciate any support!
-
--   __[Donate via PayPal](https://www.paypal.me/lazychaser)__
-
 __Contents:__
 
 - [Theory](#what-are-nested-sets)
diff --git a/composer.json b/composer.json
index ff3df84..fb15894 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
 {
-    "name": "kalnoy/nestedset",
-    "description": "Nested Set Model for Laravel 5.7 and up",
+    "name": "lychee-org/nestedset",
+    "description": "Nested Set Model for Laravel 5.7 and up (fork with patches for Lychee)",
     "keywords": ["laravel", "nested sets", "nsm", "database", "hierarchy"],
     "license": "MIT",
 
@@ -25,9 +25,19 @@
     },
 
     "require-dev": {
-        "phpunit/phpunit": "7.*|8.*|9.*"
+        "php-parallel-lint/php-parallel-lint": "^1.2",
+        "phpunit/phpunit": "^9.5.20"
     },
 
+    "scripts": {
+        "run-tests": [
+            "vendor/bin/phpunit -c phpunit.xml",
+            "vendor/bin/phpunit -c phpunit.xml --coverage-clover=coverage.xml"
+        ],
+        "validate-files": [
+            "vendor/bin/parallel-lint --exclude vendor ."
+        ]
+    },
     "minimum-stability": "dev",
     "prefer-stable": true,
 
@@ -41,10 +51,5 @@
                 "Kalnoy\\Nestedset\\NestedSetServiceProvider"
             ]
         }
-    },
-    "scripts": {
-        "test": [
-            "@php ./vendor/bin/phpunit"
-        ]
     }
 }
diff --git a/phpunit.xml b/phpunit.xml
index 1e71a58..ab23f7f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,22 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<phpunit backupGlobals="false"
-         backupStaticAttributes="false"
-         bootstrap="phpunit.php"
-         colors="true"
-         convertErrorsToExceptions="true"
-         convertNoticesToExceptions="true"
-         convertWarningsToExceptions="true"
-         processIsolation="false"
->
-    <testsuites>
-        <testsuite name="Package Test Suite">
-            <directory suffix=".php">./tests/</directory>
-        </testsuite>
-    </testsuites>
-
-    <filter>
-        <whitelist>
-            <directory>./src</directory>
-        </whitelist>
-    </filter>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="phpunit.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
+  <coverage>
+    <include>
+      <directory>./src</directory>
+    </include>
+  </coverage>
+  <testsuites>
+    <testsuite name="Package Test Suite">
+      <directory suffix=".php">./tests/</directory>
+    </testsuite>
+  </testsuites>
 </phpunit>
\ No newline at end of file
diff --git a/src/BaseRelation.php b/src/BaseRelation.php
index 031eecf..e2bd7db 100644
--- a/src/BaseRelation.php
+++ b/src/BaseRelation.php
@@ -144,7 +144,7 @@ public function addEagerConstraints(array $models)
         // The first model in the array is always the parent, so add the scope constraints based on that model.
         // @link https://github.com/laravel/framework/pull/25240
         // @link https://github.com/lazychaser/laravel-nestedset/issues/351
-        optional($models[0])->applyNestedSetScope($this->query);
+        optional(reset($models))->applyNestedSetScope($this->query);
 
         $this->query->whereNested(function (Builder $inner) use ($models) {
             // We will use this query in order to apply constraints to the
diff --git a/src/NestedSet.php b/src/NestedSet.php
index 8ec8e02..7045471 100644
--- a/src/NestedSet.php
+++ b/src/NestedSet.php
@@ -77,7 +77,7 @@ public static function getDefaultColumns()
      */
     public static function isNode($node)
     {
-        return is_object($node) && in_array(NodeTrait::class, (array)$node);
+        return $node instanceof Node;
     }
 
 }
\ No newline at end of file
diff --git a/src/Node.php b/src/Node.php
new file mode 100644
index 0000000..dfcb12f
--- /dev/null
+++ b/src/Node.php
@@ -0,0 +1,363 @@
+<?php
+
+namespace Kalnoy\Nestedset;
+
+use Illuminate\Database\Eloquent\Collection as EloquentCollection;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+
+/**
+ * Accompanies {@link \Kalnoy\Nestedset\NodeTrait}.
+ *
+ * This interface declares all public methods of a node which are implemented
+ * by {@link \Kalnoy\Nestedset\NodeTrait}.
+ *
+ * Every model which represents a node in a nested set, must realize this
+ * interface.
+ * This interface is mandatory such that
+ * {@link \Kalnoy\Nestedset\NestedSet::isNode()} recognizes an object as a
+ * node.
+ */
+interface Node
+{
+	/**
+	 * Relation to the parent.
+	 *
+	 * @return BelongsTo
+	 */
+	public function parent();
+
+	/**
+	 * Relation to children.
+	 *
+	 * @return HasMany
+	 */
+	public function children();
+
+	/**
+	 * Get query for descendants of the node.
+	 *
+	 * @return DescendantsRelation
+	 */
+	public function descendants();
+
+	/**
+	 * Get query for siblings of the node.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function siblings();
+
+	/**
+	 * Get the node siblings and the node itself.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function siblingsAndSelf();
+
+	/**
+	 * Get query for the node siblings and the node itself.
+	 *
+	 * @param array $columns
+	 *
+	 * @return EloquentCollection
+	 */
+	public function getSiblingsAndSelf(array $columns = ['*']);
+
+	/**
+	 * Get query for siblings after the node.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function nextSiblings();
+
+	/**
+	 * Get query for siblings before the node.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function prevSiblings();
+
+	/**
+	 * Get query for nodes after current node.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function nextNodes();
+
+	/**
+	 * Get query for nodes before current node in reversed order.
+	 *
+	 * @return QueryBuilder
+	 */
+	public function prevNodes();
+
+	/**
+	 * Get query ancestors of the node.
+	 *
+	 * @return AncestorsRelation
+	 */
+	public function ancestors();
+
+	/**
+	 * Make this node a root node.
+	 *
+	 * @return $this
+	 */
+	public function makeRoot();
+
+	/**
+	 * Save node as root.
+	 *
+	 * @return bool
+	 */
+	public function saveAsRoot();
+
+	/**
+	 * @param $lft
+	 * @param $rgt
+	 * @param $parentId
+	 *
+	 * @return $this
+	 */
+	public function rawNode($lft, $rgt, $parentId);
+
+	/**
+	 * Move node up given amount of positions.
+	 *
+	 * @param int $amount
+	 *
+	 * @return bool
+	 */
+	public function up($amount = 1);
+
+	/**
+	 * Move node down given amount of positions.
+	 *
+	 * @param int $amount
+	 *
+	 * @return bool
+	 */
+	public function down($amount = 1);
+
+	/**
+	 * @since 2.0
+	 */
+	public function newEloquentBuilder($query);
+
+	/**
+	 * Get a new base query that includes deleted nodes.
+	 *
+	 * @since 1.1
+	 *
+	 * @return QueryBuilder
+	 */
+	public function newNestedSetQuery($table = null);
+
+	/**
+	 * @param ?string $table
+	 *
+	 * @return QueryBuilder
+	 */
+	public function newScopedQuery($table = null);
+
+	/**
+	 * @param mixed   $query
+	 * @param ?string $table
+	 *
+	 * @return mixed
+	 */
+	public function applyNestedSetScope($query, $table = null);
+
+	/**
+	 * @param array $attributes
+	 *
+	 * @return self
+	 */
+	public static function scoped(array $attributes);
+
+	public function newCollection(array $models = []);
+
+	/**
+	 * Get node height (rgt - lft + 1).
+	 *
+	 * @return int
+	 */
+	public function getNodeHeight();
+
+	/**
+	 * Get number of descendant nodes.
+	 *
+	 * @return int
+	 */
+	public function getDescendantCount();
+
+	/**
+	 * Set the value of model's parent id key.
+	 *
+	 * Behind the scenes node is appended to found parent node.
+	 *
+	 * @param int $value
+	 *
+	 * @throws \Exception If parent node doesn't exists
+	 */
+	public function setParentIdAttribute($value);
+
+	/**
+	 * Get whether node is root.
+	 *
+	 * @return bool
+	 */
+	public function isRoot();
+
+	/**
+	 * @return bool
+	 */
+	public function isLeaf();
+
+	/**
+	 * Get the lft key name.
+	 *
+	 * @return string
+	 */
+	public function getLftName();
+
+	/**
+	 * Get the rgt key name.
+	 *
+	 * @return string
+	 */
+	public function getRgtName();
+
+	/**
+	 * Get the parent id key name.
+	 *
+	 * @return string
+	 */
+	public function getParentIdName();
+
+	/**
+	 * Get the value of the model's lft key.
+	 *
+	 * @return int
+	 */
+	public function getLft();
+
+	/**
+	 * Get the value of the model's rgt key.
+	 *
+	 * @return int
+	 */
+	public function getRgt();
+
+	/**
+	 * Get the value of the model's parent id key.
+	 *
+	 * @return int
+	 */
+	public function getParentId();
+
+	/**
+	 * Returns node that is next to current node without constraining to siblings.
+	 *
+	 * This can be either a next sibling or a next sibling of the parent node.
+	 *
+	 * @param array $columns
+	 *
+	 * @return self
+	 */
+	public function getNextNode(array $columns = ['*']);
+
+	/**
+	 * Returns node that is before current node without constraining to siblings.
+	 *
+	 * This can be either a prev sibling or parent node.
+	 *
+	 * @param array $columns
+	 *
+	 * @return self
+	 */
+	public function getPrevNode(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Collection
+	 */
+	public function getAncestors(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Collection|self[]
+	 */
+	public function getDescendants(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Collection|self[]
+	 */
+	public function getSiblings(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Collection<self>
+	 */
+	public function getNextSiblings(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Collection<self>
+	 */
+	public function getPrevSiblings(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Node
+	 */
+	public function getNextSibling(array $columns = ['*']);
+
+	/**
+	 * @param array $columns
+	 *
+	 * @return Node
+	 */
+	public function getPrevSibling(array $columns = ['*']);
+
+	/**
+	 * @return array<int>
+	 */
+	public function getBounds();
+
+	/**
+	 * @param $value
+	 *
+	 * @return $this
+	 */
+	public function setLft($value);
+
+	/**
+	 * @param $value
+	 *
+	 * @return $this
+	 */
+	public function setRgt($value);
+
+	/**
+	 * @param $value
+	 *
+	 * @return $this
+	 */
+	public function setParentId($value);
+
+	/**
+	 * @param array|null $except
+	 *
+	 * @return $this
+	 */
+	public function replicate(array $except = null);
+}
diff --git a/src/NodeTrait.php b/src/NodeTrait.php
index 0b985ab..5e9ee78 100644
--- a/src/NodeTrait.php
+++ b/src/NodeTrait.php
@@ -49,10 +49,9 @@ public static function bootNodeTrait()
 
         static::deleting(function ($model) {
             // We will need fresh data to delete node safely
+            // We must delete the descendants BEFORE we delete the actual
+            // album to avoid failing FOREIGN key constraints.
             $model->refreshNode();
-        });
-
-        static::deleted(function ($model) {
             $model->deleteDescendants();
         });
 
@@ -628,7 +627,31 @@ protected function deleteDescendants()
             ? 'forceDelete'
             : 'delete';
 
-        $this->descendants()->{$method}();
+        // We must delete the nodes in correct order to avoid failing
+        // foreign key constraints when we delete an entire subtree.
+        // For MySQL we must avoid that a parent is deleted before its
+        // children although the complete subtree will be deleted eventually.
+        // Hence, deletion must start with the deepest node, i.e. with the
+        // highest _lft value first.
+        // Note: `DELETE ... ORDER BY` is non-standard SQL but required by
+        // MySQL (see https://dev.mysql.com/doc/refman/8.0/en/delete.html),
+        // because MySQL only supports "row consistency".
+        // This means the DB must be consistent before and after every single
+        // operation on a row.
+        // This is contrasted by statement and transaction consistency which
+        // means that the DB must be consistent before and after every
+        // completed statement/transaction.
+        // (See https://dev.mysql.com/doc/refman/8.0/en/ansi-diff-foreign-keys.html)
+        // ANSI Standard SQL requires support for statement/transaction
+        // consistency, but only PostgreSQL supports it.
+        // (Good PosgreSQL :-) )
+        // PostgreSQL does not support `DELETE ... ORDER BY` but also has no
+        // need for it.
+        // The grammar compiler removes the superfluous "ORDER BY" for
+        // PostgreSQL.
+        $this->descendants()
+            ->orderBy($this->getLftName(), 'desc')
+            ->{$method}();
 
         if ($this->hardDeleting()) {
             $height = $rgt - $lft + 1;
diff --git a/tests/models/Category.php b/tests/models/Category.php
index bcce8e9..0d336f3 100644
--- a/tests/models/Category.php
+++ b/tests/models/Category.php
@@ -2,7 +2,7 @@
 
 use \Illuminate\Database\Eloquent\Model;
 
-class Category extends Model {
+class Category extends Model implements \Kalnoy\Nestedset\Node {
 
     use \Illuminate\Database\Eloquent\SoftDeletes, \Kalnoy\Nestedset\NodeTrait;
 
diff --git a/tests/models/DuplicateCategory.php b/tests/models/DuplicateCategory.php
index a6f619a..34311a9 100644
--- a/tests/models/DuplicateCategory.php
+++ b/tests/models/DuplicateCategory.php
@@ -1,6 +1,6 @@
 <?php
 
-class DuplicateCategory extends \Illuminate\Database\Eloquent\Model
+class DuplicateCategory extends \Illuminate\Database\Eloquent\Model implements \Kalnoy\Nestedset\Node
 {
     use \Kalnoy\Nestedset\NodeTrait;
 
diff --git a/tests/models/MenuItem.php b/tests/models/MenuItem.php
index 2e10a55..4c4268c 100644
--- a/tests/models/MenuItem.php
+++ b/tests/models/MenuItem.php
@@ -1,7 +1,7 @@
 <?php
 
 
-class MenuItem extends \Illuminate\Database\Eloquent\Model
+class MenuItem extends \Illuminate\Database\Eloquent\Model implements \Kalnoy\Nestedset\Node
 {
     use \Kalnoy\Nestedset\NodeTrait;