: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
namespace NinjaForms\Includes\Handlers;
use NinjaForms\Includes\Contracts\SubmissionDataSource;
use NinjaForms\Includes\Entities\SingleSubmission;
use NinjaForms\Includes\Entities\SubmissionField;
use NinjaForms\Includes\Entities\SubmissionFilter;
use NinjaForms\Includes\Handlers\Field;
* Aggregates submissions from all provided data sources
* Data sources include NF submissions stored as posts; may include pre-existing
* Caldera Forms submissions
class SubmissionAggregate
* NF Id of the master form
* There can be multiple forms in the aggregate; the master form defines the
* fields and the data within the aggregate.
protected $masterFormId = '';
/** @var SubmissionFilter */
protected $submissionFilter;
* Collection of submission data sources
* @var SubmissionDataSource[];
protected $dataSourceCollection = [];
* Collection of all SingleSubmissions
* Indexed collection of submissions meeting the filtering parameters
* @var SingleSubmission[]
protected $aggregatedSubmissions = [];
* Collection of SubmissionFields defining the form
protected $submissionFieldCollection = [];
* Filter submissions to return a collection of SingleSubmission meta data only
public function filterSubmissions(SubmissionFilter $submissionFilter): array
$this->submissionFilter = $submissionFilter;
$formIdCollection = $this->submissionFilter->getNfFormIds();
if (!empty($formIdCollection)) {
$masterFormId = $formIdCollection[0];
$this->constructFieldDefinitionCollection($masterFormId);
if (!empty($this->dataSourceCollection)) {
foreach ($this->dataSourceCollection as $dataSource) {
$submissionsFromDataSource = $dataSource->retrieveSubmissionMeta($this->submissionFilter);
$aggregated = array_merge($aggregated, $submissionsFromDataSource);
foreach ($aggregated as $singleSubmission) {
$key = $this->constructUniqueAgreggatedSubmissionKey($singleSubmission);
$submissionIDs = $this->submissionFilter->getSubmissionsIDs();
//Add singleSubmission to aggregated collection if no submissions IDs were set or if it has correct ID
if( in_array( $singleSubmission->submissionRecordId, $submissionIDs ) || empty( $submissionIDs ) ){
$this->aggregatedSubmissions[$key] = $singleSubmission;
uasort($this->aggregatedSubmissions, function ($a, $b) {
return $b->getTimestamp() <=> $a->getTimestamp();
return $this->aggregatedSubmissions;
* Retrieve a single submission populated with submission/extra values
* Also populates the submissionAggregate such that the aggregate can be
* passed for handling elsewhere
* @param SingleSubmission $singleSubmission
* @return SingleSubmission
public function requestSingleSubmission(SingleSubmission $singleSubmission): SingleSubmission
$this->masterFormId = $singleSubmission->getFormId();
$this->constructFieldDefinitionCollection($this->masterFormId);
$singleSubmission->setSubmissionFieldCollection($this->submissionFieldCollection);
$dataSourceKey = $singleSubmission->getDataSource();
if(isset($this->dataSourceCollection[$dataSourceKey])){
$dataSource=$this->dataSourceCollection[$dataSourceKey];
$singleSubmission = $dataSource->retrieveSingleSubmission($singleSubmission);
$key = $this->constructUniqueAgreggatedSubmissionKey($singleSubmission);
$this->aggregatedSubmissions[$key] = $singleSubmission;
return $singleSubmission;
* Retrieve a submissions by precise list of submissions IDs
* @param SubmissionFilter $submissionFilter
* @return SubmissionFilter
public function requestSubmissionsByIds(SubmissionFilter $submissionFilter): SubmissionFilter
$this->masterFormId = $singleSubmission->getFormId();
$this->constructFieldDefinitionCollection($this->masterFormId);
$submissions->setSubmissionFieldCollection($this->submissionFieldCollection);
$dataSourceKey = $singleSubmission->getDataSource();
if(isset($this->dataSourceCollection[$dataSourceKey])){
$dataSource=$this->dataSourceCollection[$dataSourceKey];
$singleSubmission = $dataSource->retrieveSingleSubmission($singleSubmission);
$key = $this->constructUniqueAgreggatedSubmissionKey($singleSubmission);
$this->aggregatedSubmissions[$key] = $singleSubmission;
return $singleSubmission;
* Retrieve submissionValues from submission at a given aggregated key
* @return SingleSubmission
* @see constructUniqueAgreggatedSubmissionKey()
public function getSubmissionValuesByAggregatedKey(string $key): SingleSubmission
/** @var SubmissionDataSource $dataSource */
if (isset($this->aggregatedSubmissions[$key])) {
$singleSubmission = $this->aggregatedSubmissions[$key];
if (empty($singleSubmission->getSubmissionFieldCollection())) {
$singleSubmission->setSubmissionFieldCollection($this->submissionFieldCollection);
$dataSource = $this->dataSourceCollection[$singleSubmission->getDataSource()];
$populatedSingleSubmission = $dataSource->retrieveSubmissionValues($singleSubmission);
$populatedSingleSubmission = $singleSubmission;
$populatedSingleSubmission = SingleSubmission::fromArray([]);
// create a new object to avoid object-by-reference that updates all submissions in the collection
$this->aggregatedSubmissions[$key] = SingleSubmission::fromArray($populatedSingleSubmission->toArray());
return $this->aggregatedSubmissions[$key];
* Delete a single submission
* @param SingleSubmission $singleSubmission
* @return SubmissionAggregate
public function deleteSingleSubmission(SingleSubmission $singleSubmission): SubmissionAggregate
$dataSourceKey = $singleSubmission->getDataSource();
if(isset($this->dataSourceCollection[$dataSourceKey])){
$dataSource = $this->dataSourceCollection[$dataSourceKey];
$dataSource->deleteSubmission($singleSubmission);
* Restore a single submission
* @param SingleSubmission $singleSubmission
* @return SubmissionAggregate
public function restoreSingleSubmission(SingleSubmission $singleSubmission): SubmissionAggregate
$dataSourceKey = $singleSubmission->getDataSource();
if(isset($this->dataSourceCollection[$dataSourceKey])){
$dataSource = $this->dataSourceCollection[$dataSourceKey];
$dataSource->restoreSubmission($singleSubmission);
* Update a single submission
* @param SingleSubmission $singleSubmission
* @return SubmissionAggregate
public function updateSingleSubmission(SingleSubmission $singleSubmission): SubmissionAggregate
$dataSourceKey = $singleSubmission->getDataSource();
if(isset($this->dataSourceCollection[$dataSourceKey])){
$dataSource = $this->dataSourceCollection[$dataSourceKey];
$dataSource->updateSubmission($singleSubmission);
* Construct field definition collection from formId
protected function constructFieldDefinitionCollection(string $formId): void
$nfFieldsCollection = $this->getFieldsCollection($formId);
if (!empty($nfFieldsCollection)) {
/** @var Field $nfField */
foreach ($nfFieldsCollection as $id => $nfField) {
$slug = $nfField->get_setting('key');
$fieldSettings = $nfField->get_settings();
$fieldOptionDefinition = $nfField->get_setting('options',[]);
$fieldsetRepeaterFields = $nfField->get_setting('fields',[]);
if(!empty($fieldOptionDefinition)){
foreach($fieldOptionDefinition as $optionDefinition){
$options = $this->extractOptionsFromDefinition($optionDefinition);
$optionsCollection[]=$options;
'label' => $nfField->get_setting('label'),
'adminLabel' => $nfField->get_setting('admin_label'),
'type' => $nfField->get_setting('type'),
'options' => $optionsCollection,
'fieldsetRepeaterFields'=>$fieldsetRepeaterFields,
'original' => $fieldSettings
$this->submissionFieldCollection[$slug] = SubmissionField::fromArray($array);
* Extract option array from a given array
* Ensures that required defaults are set if missing in incoming array
* @param array $optionDefinition
protected function extractOptionsFromDefinition(array $optionDefinition): array
$return = array_merge($defaults, \array_intersect_key($optionDefinition, $defaults));
* Return the Ninja Forms field collection
protected function getFieldsCollection(string $formId): array
$return = \Ninja_Forms()->form($formId)->get_fields();
* Construct a unique aggregated submission key for each submission
* Uses the dataSource's id plus the submission record id. Each submission
* is is unique within its dataSource, and each dataSource is unique, thus
* the combined string is unique
* @param SingleSubmission $singleSubmission
protected function constructUniqueAgreggatedSubmissionKey(SingleSubmission $singleSubmission): string
$key = $singleSubmission->getDataSource() . '__' . $singleSubmission->getSubmissionRecordId();
* Set collection of submission data sources
* @param SubmissionDataSource $dataSource Submission data source
* @return SubmissionAggregate
public function addDataSource(SubmissionDataSource $dataSource): SubmissionAggregate
$this->dataSourceCollection[$dataSource->getDataSource()] = $dataSource;
* Get collection of SubmissionFields
* @return SubmissionField[]
public function getFieldDefinitionCollection(): array
return $this->submissionFieldCollection;
public function getSubmissionCount(): int
return count($this->aggregatedSubmissions);
* Get indexed collection of submissions meeting the filtering parameters
* @return SingleSubmission[]
public function getAggregatedSubmissions(): array
return $this->aggregatedSubmissions;
* Get fields and the data within the aggregate.
public function getMasterFormId(): string
return $this->masterFormId;
* Set keyed collection of submissions
* This method enables re-setting the aggregated submissions after
* performing array methods on it. This is useful to get a subset of the
* collection without needing to re-filter and run DB requests
* @param Array $aggregatedSubmissions Keyed collection of
* submissions meeting the filtering parameters
public function setAggregatedSubmissions(Array $aggregatedSubmissions)
$this->aggregatedSubmissions = $aggregatedSubmissions;