# ivyforms/0.4/backend/src/Services/Entry/EntryService.php

The Innovative Form Builder – IvyForms, version 0.4. 387 lines.

- Page: https://pluginprobe.com/plugins/ivyforms/0.4/code/backend/src/Services/Entry/EntryService.php
- Raw: https://pluginprobe.com/plugins/ivyforms/0.4/raw/backend/src/Services/Entry/EntryService.php
- Modified: 2025-09-25T14:53:02+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/ivyforms/0.4/code/backend/src/Services/Entry/EntryService.php#L10-L20`.

```php
<?php

namespace IvyForms\Services\Entry;

// phpcs:disable PSR1.Files.SideEffects
if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

use IvyForms\Common\Exceptions\NotFoundException;
use IvyForms\Common\Exceptions\InvalidArgumentException;
use IvyForms\Common\Exceptions\QueryExecutionException;
use IvyForms\Common\Exceptions\ValidationException;
use IvyForms\Entity\Entry\Entry;
use IvyForms\Factory\EntryField\EntryFieldFactory;
use IvyForms\Repository\Entry\EntryRepositoryInterface;
use IvyForms\Repository\EntryField\EntryFieldRepositoryInterface;
use IvyForms\Services\Translations\BackendStrings;

/**
 * @SuppressWarnings("TooManyPublicMethods")
 */
class EntryService
{
    private EntryRepositoryInterface $entryRepository;
    private EntryFieldRepositoryInterface $entryFieldRepository;

    // Constructor injection for the FormRepository via PHP-DI
    public function __construct(
        EntryRepositoryInterface $entryRepository,
        EntryFieldRepositoryInterface $entryFieldRepository
    ) {
        $this->entryRepository = $entryRepository;
        $this->entryFieldRepository = $entryFieldRepository;
    }

    /**
     * Create a new entry and its entry fields.
     *
     * @param Entry $entryData
     *
     * @return int Entry ID
     */
    public function createEntry(Entry $entryData): int
    {
        // Insert entry and get ID
        return $this->entryRepository->add($entryData);
    }

    /**
     * Add entry fields to the repository.
     *
     * @param array<int, object> $formFields
     * @param int $entryId
     * @param array<mixed> $submissionData
     * @throws ValidationException
     */
    public function addEntryFields(array $formFields, int $entryId, array $submissionData): void
    {
        $parentChildrenMap = $this->buildParentChildrenMap($formFields);
        $parentSubfieldKeys = $this->buildParentSubfieldKeys($parentChildrenMap, $submissionData);

        foreach ($formFields as $field) {
            $value = $this->resolveFieldValue($field, $submissionData, $parentChildrenMap, $parentSubfieldKeys);
            $entryField = EntryFieldFactory::create([
                'entryId'   => $entryId,
                'fieldId'    => $field->getId(),
                'fieldValue' => $value,
            ]);
            $this->entryFieldRepository->add($entryField);
        }
    }

    /**
     * Build parent-children map.
     *
     * @param array<int, object> $formFields
     * @return array<int, array<int>>
     */
    private function buildParentChildrenMap(array $formFields): array
    {
        $parentChildrenMap = [];
        foreach ($formFields as $field) {
            $parentId = method_exists($field, 'getParentId') ? $field->getParentId() : null;
            if ($parentId && $parentId > 0) {
                $parentChildrenMap[$parentId][] = $field->getId();
            }
        }
        return $parentChildrenMap;
    }

    /**
     * Build subfield key map for each parent.
     *
     * @param array<int, array<int>> $parentChildrenMap
     * @param array<mixed> $submissionData
     * @return array<int, string|null>
     */
    private function buildParentSubfieldKeys(array $parentChildrenMap, array $submissionData): array
    {
        $parentSubfieldKeys = [];
        foreach ($parentChildrenMap as $parentId => $childIds) {
            $parentValue = $submissionData[$parentId] ?? [];
            $keys = array_keys($parentValue);
            foreach ($childIds as $i => $childId) {
                $parentSubfieldKeys[$childId] = $keys[$i] ?? null;
            }
        }
        return $parentSubfieldKeys;
    }

    /**
     * Resolve the value for a field based on its type and parent/child relationship.
     *
     * @param object $field
     * @param array<mixed> $submissionData
     * @param array<int, array<int>> $parentChildrenMap
     * @param array<int, string|null> $parentSubfieldKeys
     * @return mixed
     */
    private function resolveFieldValue(
        object $field,
        array $submissionData,
        array $parentChildrenMap,
        array $parentSubfieldKeys
    ) {
        $fieldId = $field->getId();
        $fieldType = $field->getType();
        $parentId = method_exists($field, 'getParentId') ? $field->getParentId() : null;

        $parentValue = $this->getParentSubfieldValue($fieldId, $parentId, $submissionData, $parentSubfieldKeys);
        if ($parentValue !== null) {
            return $parentValue;
        }

        if (in_array($fieldType, ['name', 'address'], true) && ($parentId === null || $parentId === 0)) {
            return $this->getCompoundFieldValue($fieldId, $parentChildrenMap, $parentSubfieldKeys, $submissionData);
        }

        return $this->getDefaultFieldValue($fieldId, $submissionData);
    }

    /**
     * @param int $fieldId
     * @param int|null $parentId
     * @param array<int|string, mixed> $submissionData
     * @param array<int, string|null> $parentSubfieldKeys
     * @return mixed|null
     */
    private function getParentSubfieldValue(
        int $fieldId,
        ?int $parentId,
        array $submissionData,
        array $parentSubfieldKeys
    ) {
        if ($parentId && isset($parentSubfieldKeys[$fieldId])) {
            $subKey = $parentSubfieldKeys[$fieldId];
            if (isset($submissionData[$parentId][$subKey])) {
                return $submissionData[$parentId][$subKey];
            }
        }
        return null;
    }

    /**
     * @param int $fieldId
     * @param array<int, array<int>> $parentChildrenMap
     * @param array<int, string|null> $parentSubfieldKeys
     * @param array<int|string, mixed> $submissionData
     * @return string
     */
    private function getCompoundFieldValue(
        int $fieldId,
        array $parentChildrenMap,
        array $parentSubfieldKeys,
        array $submissionData
    ): string {
        $childValues = [];
        if (!empty($parentChildrenMap[$fieldId])) {
            foreach ($parentChildrenMap[$fieldId] as $childId) {
                $subKey = $parentSubfieldKeys[$childId] ?? null;
                if ($subKey && isset($submissionData[$fieldId][$subKey])) {
                    $val = $submissionData[$fieldId][$subKey];
                    if ($val !== '') {
                        $childValues[] = $val;
                    }
                }
            }
        }
        return implode(' ', $childValues);
    }

    /**
     * @param int $fieldId
     * @param array<int|string, mixed> $submissionData
     * @return mixed|string
     */
    private function getDefaultFieldValue(int $fieldId, array $submissionData)
    {
        return $submissionData[$fieldId] ?? '';
    }

    /**
     * Get a specific entry by its ID.
     *
     * @param int $entryId
     *
     * @return object
     *
     * @throws NotFoundException If the entry is not found
     */
    public function getEntry(int $entryId): object
    {
        $entry = $this->entryRepository->getById($entryId);

        if (!$entry) {
            throw new NotFoundException(
                BackendStrings::getExceptionStrings()['entry_not_found']
            );
        }

        return $entry;
    }

    /**
     * Search for entries based on parameters.
     *
     * @param array<string, mixed> $params
     *
     * @return array<mixed>
     */
    public function searchEntries(array $params): array
    {
        return $this->entryRepository->search($params);
    }

    /**
     * Get the count of entry filters.
     *
     * @param array<string, mixed>|null $params
     *
     * @return array<mixed>
     */
    public function getFilterCount(?array $params = null): array
    {
        return $this->entryRepository->getFilterCount($params);
    }

    /**
     * Delete an entry by its ID.
     *
     * @param int $id
     *
     * @return int
    */
    public function deleteEntry(int $id): int
    {
        return $this->entryRepository->delete($id);
    }

    /**
     * Get all entry fields for a specific entry ID.
     *
     * @param int $entryId
     *
     * @return array<mixed>
     */
    public function getFieldsByEntryId(int $entryId): array
    {
        return $this->entryFieldRepository->findBy(['entryId' => $entryId]);
    }

    /**
     * Update the starred status of an entry.
     *
     * @param int $entryId
     * @param bool $value
     *
     * @return void
     */
    public function updateEntryStarred(int $entryId, bool $value): void
    {
        $this->entryRepository->updateEntryStarred($entryId, $value);
    }

    /**
     * Update the status of an entry.
     *
     * @param int $entryId
     * @param string $status
     *
     * @return void
     */
    public function updateEntryStatus(int $entryId, string $status): void
    {
        $this->entryRepository->updateEntryStatus($entryId, $status);
    }

    /**
     * Fetch entries along with their associated fields.
     *
     * @param array<int, array<string, mixed>> $entries
     * @return array<mixed>
     */
    public function getEntryFields(array $entries): array
    {
        return $this->entryRepository->getEntryFields($entries);
    }

    /**
     * Get the count of entries for multiple form IDs.
     *
     * @param int[] $formIds
     * @return array<int, int> formId => count
     */
    public function getEntryCountByFormIds(array $formIds): array
    {
        return $this->entryRepository->getEntryCountByFormIds($formIds);
    }

    /**
     * Delete all entry fields for multiple entry IDs in bulk.
     *
     * @param int[] $entryIds
     * @return int Number of deleted entry fields
     */
    public function deleteEntryFieldsByEntryIds(array $entryIds): int
    {
        if (empty($entryIds)) {
            return 0;
        }
        $allFields = $this->entryFieldRepository->findIdsByEntryIds($entryIds);
        if (empty($allFields)) {
            return 0;
        }
        return $this->entryFieldRepository->deleteMany($allFields);
    }

    /**
     * Delete multiple entries by their IDs in bulk.
     *
     * @param int[] $ids
     * @return int Number of deleted entries
     */
    public function deleteEntriesByIds(array $ids): int
    {
        if (empty($ids)) {
            return 0;
        }
        return $this->entryRepository->deleteMany($ids);
    }

    /**
     * Delete all entries for multiple form IDs in bulk.
     *
     * @param int[] $formIds
     * @return int Number of deleted entries
     * @throws InvalidArgumentException
     */
    public function deleteEntriesByFormIds(array $formIds): int
    {
        if (empty($formIds)) {
            throw new InvalidArgumentException(
                BackendStrings::getExceptionStrings()['no_form_ids_provided']
            );
        }

        // Delete entry fields by form IDs using a subquery
        $this->entryFieldRepository->deleteByFormIds($formIds);
        // Delete entries by formId
        return $this->entryRepository->deleteManyByForeignKeyValues($formIds);
    }

    /**
     * Check if a value already exists in entry fields for a specific field.
     *
     * @param int $formId
     * @param int $fieldId
     * @param string $value
     * @return bool
     */
    public function checkDuplicateValue(int $formId, int $fieldId, string $value): bool
    {
        return $this->entryFieldRepository->checkDuplicateValue($formId, $fieldId, $value);
    }
}

```
