1. Go to this page and download the library: Download tuzelko/yii2-softdelete library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
tuzelko / yii2-softdelete example snippets
// Unix timestamp (default)
$this->addColumn('{{%post}}', 'deleted_at', $this->integer()->null()->defaultValue(null));
// — or — boolean flag
$this->addColumn('{{%article}}', 'is_deleted', $this->boolean()->notNull()->defaultValue(false));
use tuzelko\yii\softdelete\SoftDeleteTrait;
use yii\db\ActiveRecord;
class Post extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string
{
return 'post';
}
}
class Article extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string { return 'article'; }
public static function softDeleteColumn(): string { return 'is_deleted'; }
public static function softDeleteType(): int { return self::TYPE_BOOL; }
}
$post = Post::findOne(1);
$post->softDelete(); // soft-delete — sets deleted_at, hides from default scope
$post->isSoftDeleted(); // true
$post->restore(); // clears deleted_at, record becomes visible again
$post->isSoftDeleted(); // false
$post->hardDelete(); // permanent hard-delete (fires standard Yii2 before/afterDelete events)
// Default — excludes soft-deleted records (no extra call needed)
Post::find()->all();
// Include soft-deleted records alongside active ones
Post::find()->withDeleted()->all();
// Only soft-deleted records
Post::find()->onlyDeleted()->all();
// Soft-delete all active records matching the condition
Post::softDeleteAll(['status' => 'spam']);
// Restore all soft-deleted records
Post::restoreAll();
// Restore specific records
Post::restoreAll(['id' => [3, 5, 7]]);
// Permanently delete all soft-deleted records
Post::hardDeleteAll(['is not', 'deleted_at', null]);
// updateAll() also skips soft-deleted records automatically
Post::updateAll(['status' => 'archived'], ['category_id' => 2]);
class Post extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string { return 'post'; }
// Always route delete() to hardDelete()
public static function defaultDeleteMethod(): int
{
return self::DELETE_METHOD_HARD;
}
}
class Post extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string { return 'post'; }
// Disable delete() entirely — callers must choose softDelete() or hardDelete() explicitly
public static function defaultDeleteMethod(): int
{
return self::DELETE_METHOD_DISABLED;
}
}
class User extends ActiveRecord
{
// hasMany — only active (non-deleted) posts are returned
public function getPosts(): SoftDeleteActiveQuery
{
return $this->hasMany(Post::class, ['user_id' => 'id']);
}
// To uery
{
return $this->hasOne(Post::class, ['user_id' => 'id'])
->orderBy(['created_at' => SORT_DESC]);
}
}
class Post extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string { return 'post'; }
// Relation to a model that does NOT use soft-delete — works as usual
public function getUser(): \yii\db\ActiveQuery
{
return $this->hasOne(User::class, ['id' => 'user_id']);
}
// Relation to another soft-delete model
public function getComments(): SoftDeleteActiveQuery
{
return $this->hasMany(Comment::class, ['post_id' => 'id']);
}
}
// Load users and only their active posts (soft-delete scope applied automatically)
$users = User::find()->with('posts')->all();
foreach ($users as $user) {
foreach ($user->posts as $post) {
// $post is never soft-deleted
}
}
// Load users together with ALL their posts (including deleted)
$users = User::find()
->with(['posts' => fn($q) => $q->withDeleted()])
->all();
// Load users with only their deleted posts
$users = User::find()
->with(['posts' => fn($q) => $q->onlyDeleted()])
->all();
// LEFT JOIN (default) — all users appear; only active posts are joined
// Generated SQL: ... LEFT JOIN post ON user.id = post.user_id AND post.deleted_at IS NULL
// WHERE user.deleted_at IS NULL
$users = User::find()->joinWith('posts')->all();
// INNER JOIN — only users with at least one active post are returned
$users = User::find()->joinWith('posts', false, 'INNER JOIN')->all();
// Include deleted posts in the JOIN
$users = User::find()->joinWith(['posts' => fn($q) => $q->withDeleted()])->all();
// JOIN only with deleted posts (e.g. to find users with pending cleanup)
$users = User::find()
->joinWith(['posts' => fn($q) => $q->onlyDeleted()], false, 'INNER JOIN')
->all();
$post->on(Post::EVENT_BEFORE_SOFT_DELETE, function (\yii\base\ModelEvent $event) {
if (!Yii::$app->user->can('deletePost')) {
$event->isValid = false; // cancel the soft-delete
}
});
// In your migration
$this->createIndex('idx_post_deleted_at', 'post', 'deleted_at');
// Boolean column — a partial index (supported by PostgreSQL and SQLite) is even more efficient
$this->createIndex('idx_article_is_deleted', 'article', 'is_deleted');
class Post extends ActiveRecord
{
use SoftDeleteTrait;
public static function tableName(): string { return 'post'; }
public function afterSoftDelete(): void
{
Comment::softDeleteAll(['post_id' => $this->id]);
}
public function afterRestore(): void
{
Comment::restoreAll(['post_id' => $this->id]);
}
}
// ✗ scope is added twice — the string is not inspected
Post::softDeleteAll("deleted_at IS NULL AND category_id = 5");
// ✓ wrap in an array so the column is detected
Post::softDeleteAll(['and', ['is', 'deleted_at', null], ['category_id' => 5]]);
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.