PHPackages                             littlegiant/silverstripe-batchwrite - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. littlegiant/silverstripe-batchwrite

ActiveSilverstripe-module[Database &amp; ORM](/categories/database)

littlegiant/silverstripe-batchwrite
===================================

Batch write for silverstripe data objects

0.3.0(10y ago)48.2k5[1 PRs](https://github.com/isobar-nz/silverstripe-batchwrite/pulls)1BSD-2-ClausePHP

Since Oct 8Pushed 7y agoCompare

[ Source](https://github.com/isobar-nz/silverstripe-batchwrite)[ Packagist](https://packagist.org/packages/littlegiant/silverstripe-batchwrite)[ Docs](http://github.com/Little-Giant/silverstripe-batchwrite)[ RSS](/packages/littlegiant-silverstripe-batchwrite/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (11)Used By (1)

silverstripe-batchwrite
-----------------------

[](#silverstripe-batchwrite)

Batchwrite data objects to improve bulk insert/update/delete performance. **Currently supports MySQL and only when the `mysqli` adapater is used.**

- [Basic usage](#user-content-basic-usage)
- [Abstracting batch by using BatchedWriter](#user-content-abstracting-batch-by-using-batchedwriter)
- [Dealing with has\_one, has\_many and many\_many](#user-content-dealing-with-has_one-has_many-and-many_many)
- [Squeezing out more performance](#user-content-squeezing-out-more-performance)
- [Want to contribute or need another feature?](#user-content-want-to-contribute-or-need-another-feature)

Basic Usage
-----------

[](#basic-usage)

```
$batch = new Batch();

$objects = ...;
$batch->write($objects);

$versionedObjects = ...;
$batch->writeToStage($versionedObjects, 'Stage');

$objectsToDelete = ...;
$batch->delete($objectsToDelete);

$versionedObjectsToDelete = ...;
$batch->deleteFromStage($versionedObjectsToDelete, 'Stage');

$class = 'DataObjectClass';
$ids = array(1, 2, 3...)
$batch->deleteIDs($class, $ids);

$class = 'VersionedClass';
$batch->deleteIDsFromStage($versionedClass, $ids);

```

### write($dataObjects)

[](#writedataobjects)

Groups the data objects by class and then inserts/updates the objects using one query. This **will** call `onBeforeWrite()` and `onAfterWrite()`. It will also populate the IDs of new data objects

```
$batch = new Batch();
$batch->write($objects);

```

### writeToStage($dataObjects, $stage...)

[](#writetostagedataobjects-stage)

Same as `write($dataObjects)` but allows you to specify stages to write objects that extend `Versioned`. You can pass in more than one stage. For example -

```
$batch = new Batch();
$batch->writeToStage($objects, 'Stage'); # writes to the 'Stage'
$batch->writeToStage($objects, 'Live'); # writes to 'Live' e.g publish()
$batch->writeToStage($objects, 'Stage', 'Live'); # writes to 'Stage' and 'Live'

```

### writeManyMany($sets)

[](#writemanymanysets)

Write many many relationships, where `$sets` is an array of "`$set`". Each `$set` is an `array($object, $relation, $belongsObject)`. **This will not check if the relationship already exists, and will add it again**

```
# team->many_many = array('TeamMembers' => 'Person')
# person->belongs_many_many = array('Teams' => 'Team')
$batch = new Batch();
$team = new Team();
$person = new Person();

$batch->write($team);
$batch->write($person);
$sets = array();
$sets[] = array($team, 'TeamMembers', $person);
$batch->writeManyMany($sets);

```

### delete($dataObjects)

[](#deletedataobjects)

Groups the data objects by class then deletes the objects using one query per class/table. This **will not** call `onBeforeDelete` or `onAfterDelete`. No child objects will be deleted either, e.g `SiteTree->Children()` will be orphaned. The versions table will als0 not be updated.

```
$objects = DataObjectClass::get();
$batch = new Batch();
$batch->delete($batch);

```

### deleteFromStage($dataObjects, $stage...)

[](#deletefromstagedataobjects-stage)

Same as `delete($dataObjects)` but adds support to delet versioned objects. You can pass multiple stages at once.

```
$objects = VersionedObject::get();
$batch = new Batch();
$batch->deleteToStage($objects, 'Stage'); # deletes from the 'Stage'
$batch->deleteToStage($objects, 'Live'); # deletes from 'Live' e.g doUnpublish()
$batch->deleteToStage($objects, 'Stage', 'Live'); # deletes from 'Stage' and 'Live'

```

### deleteIDs($className, $ids)

[](#deleteidsclassname-ids)

Delete the data objects for `$className` with the given `$ids`. As per `delete($objects)` this **will not** call no `onBeforeDelete` or `onAfterDelete`. This can be used to avoid populating data objects that are just going to be deleted.

```
$className = 'DataObjectClass';
$ids = $className::get()->column('ID');
$batch = new Batch();
$batch->deleteIDs($className, $ids);

```

### deleteIDsFromStage($className, $ids, $stage...)

[](#deleteidsfromstageclassname-ids-stage)

As per `deleteIDs` but allows you to specify stages to delete from. For example -

```
$className = 'VersionedDataObjectClass';
$ids = $className::get()->column('ID');
$batch = new Batch();
$batch->deleteIDsFromStage($className, $ids, 'Stage'); # deleted from 'Stage'
$batch->deleteIDsFromStage($className, $ids, 'Live'); # deleted from 'Live'
$batch->deleteIDsFromStage($className, $ids, 'Stage', 'Live'); # 'Stage' and 'Live'

```

Abstracting batch by using BatchedWriter
----------------------------------------

[](#abstracting-batch-by-using-batchedwriter)

The `BatchedWriter` class allows you to pass data objects in one at the time, which will then be written when the batch reaches a specified size. The code below will write 100 objects at a time, with the remaining 50 objects written when `finish()` is called.

```
$writer = new BatchedWriter(100);

for ($i = 0; $i < 350; $i++) {
    $object = new DataObjectClass();
    $writer->write($object);
}

$writer->finish();

```

### Methods

[](#methods)

The `BatchedWriter` class has the same methods as `Batch` but replaces $objects with $object as the first parameter. The `finish()` method should be called to write/delete the remaining objects.

```
$writer = new BatchedWriter();

$writer->write($object);
$writer->writeToStage($object, $stage...);

$writer->delete($object);
$writer->deleteFromStage($object, $stage...);

$writer->deleteIDs($className, $id);
$writer->deleteFromStage($className, $id, $stage...);

$writer->finish(); # write/delete all the remaining objects

```

The largest difference is `writeManyMany($object, $relation, $belongsObjects)`

```
$writer = new BatchedWriter();
$team = new Team();
$person1 = new Person();
$person2 = new Person();

$writer->write(array($team, $person1, $person2));

$afterExists = new OnAfterExists(function () use ($team, $person1, $person2, $writer) {
    $writer->writeManyMany($team, 'TeamMembers', array($person1, $person2));
});
$afterExists->addCondition($team);
$afterExists->addCondition($person1);
$afterExists->addCondition($person2);

$writer->finish();

```

Dealing with has\_one, has\_many and many\_many
-----------------------------------------------

[](#dealing-with-has_one-has_many-and-many_many)

To reduce the number of writes and help abstract the complexities of writing objects in the correct order a few helper methods can be added to DataObject through the `WriteCallbackExtesion`.

```
---
Name: mysite-confg
---

...

DataObject:
    extensions:
        - WriteCallbackExtension

```

This adds `onBeforeWriteCallback`, `onAfterWriteCallback` and `onAfterExistsCallback`.

### has\_one

[](#has_one)

```
$writer = new BatchedWriter();

$team = new Team(); # has_one = array('Leader' => 'TeamLeader')
$team->Title = 'Team Spot';

$leader = new TeamLeader();
$leader->Name = 'Big Red';

$leader->onAfterExistsCallback(function ($leader) use ($writer, $team) {
    $team->LeaderID = $leader->ID;
    # write team when leader has been written
    $writer->write($team);
});

# write leader first
$writer->write($leader);
...
$writer->finish();

```

To support multiple has\_one objects you can use a `OnAfterExists`

```
$writer = new BatchedWriter();
$object = new Object(); # has_one = array('HasOne1', 'HasOne2')
$hasOne1 = new HasOne1();
$hasOne2 = new HasOne2();

# create onAfterExists to write $object when conditions are met
$afterExists = new OnAfterExists(function () use ($writer, $object) {
    $writer->write($object);
});

# add condition that hasOne1 exists
$afterExists->condition($hasOne1, function ($hasOne1) use ($object) {
    $object->HasOne1 = $hasOne1->ID;
});

# add condition that hasOne2 exists
$afterExists->condition($hasOne2, function ($hasOne2) use ($object) {
    $object->HasOne2 = $hasOne2->ID;
});

# write has_ones
$writer->write($hasOne1);
$writer->write($hasOne2);
...
$writer->finish();

```

### has\_many

[](#has_many)

In a similar fashion use `onAfterExistsCallback` to write has\_many objects

```
$writer = new BatchedWriter();

$team = new Team(); # has_many = array('TeamMembers' => 'TeamMember')

for ($i = 0; $i < 10; $i++) {
    $member = new TeamMember(); # has_one = array('Team' => 'Team');
    $team->onAfterExistsCallback(function ($team) use ($member, $writer) {
        $member->TeamID = $team->ID;
        $writer->write($team);
    });
}

$writer->write($team);
$writer->finish();

```

### many\_many

[](#many_many)

Use `OnAfterExists` to wait until all the objects exist before writing the relationship

```
$writer = new BatchedWriter();
$team = new Team();
$person1 = new Person();
$person2 = new Person();

$writer->write(array($team, $person1, $person2));

# see below
$afterExists = new OnAfterExists(function () use ($team, $person1, $person2, $writer) {
    $writer->writeManyMany($team, 'TeamMembers', array($person1, $person2));
});
$afterExists->addCondition($team);
$afterExists->addCondition($person1);
$afterExists->addCondition($person2);

$writer->finish();

```

Squeezing out more performance
------------------------------

[](#squeezing-out-more-performance)

Silverstripe data objects and extensions are very powerful. But with that power comes great overhead. A easily avoided overhead is instantiating a new DataObject. To avoid executing the same code for a large number of objects it's more efficient to create a single instance then clone it multiple times. This is easy with the `QuickDataObject` class. **WARNING: this will only call the constructor one time, do not use when you need execute the constructor for every instance**

```
$className = 'SomeDataObject';
for ($i = 0; $i < 1000; $i++) {
    $object = QuickDataObject::create($className);
}

```

Want to contribute or need another feature?
-------------------------------------------

[](#want-to-contribute-or-need-another-feature)

Contributions are always welcome, please create a pull request and I will review and merge when I get a chance. If you have a new feature request or suggestion, create an issue and I'll look into it!

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity25

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~12 days

Recently: every ~18 days

Total

7

Last Release

3802d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a499234983660ff4b31982de5e17e147544b717227fc4f7245070462fd03cbca?d=identicon)[stevie-mayhew](/maintainers/stevie-mayhew)

![](https://www.gravatar.com/avatar/13a1b78f31cdad3eecbe5d11aadc3f5c527f76cde37710ab91dc03c42565f152?d=identicon)[RyanPotter](/maintainers/RyanPotter)

---

Top Contributors

[![chriswbarrett](https://avatars.githubusercontent.com/u/3271823?v=4)](https://github.com/chriswbarrett "chriswbarrett (1 commits)")[![solid-ahlholm](https://avatars.githubusercontent.com/u/50011518?v=4)](https://github.com/solid-ahlholm "solid-ahlholm (1 commits)")[![stevie-mayhew](https://avatars.githubusercontent.com/u/1953220?v=4)](https://github.com/stevie-mayhew "stevie-mayhew (1 commits)")

---

Tags

performancesilverstripebatch

### Embed Badge

![Health badge](/badges/littlegiant-silverstripe-batchwrite/health.svg)

```
[![Health](https://phpackages.com/badges/littlegiant-silverstripe-batchwrite/health.svg)](https://phpackages.com/packages/littlegiant-silverstripe-batchwrite)
```

###  Alternatives

[silverstripe/staticpublishqueue

Static publishing queue to create static versions of pages for enhanced performance and security

45135.4k4](/packages/silverstripe-staticpublishqueue)[patricknelson/silverstripe-migrations

SilverStripe Database Migration module. Facilitates atomic database migrations in SilverStripe 3.x

2126.7k](/packages/patricknelson-silverstripe-migrations)[silverstripe/postgresql

SilverStripe now has tentative support for PostgreSQL ('Postgres')

16258.1k2](/packages/silverstripe-postgresql)[brettt89/silverstripe-garbage-collector

SilverStripe Garbage Collector module

109.6k](/packages/brettt89-silverstripe-garbage-collector)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
