<commit_before><?php
namespace Mongolid\DataMapper;

use InvalidArgumentException;
use MongoDB\BSON\ObjectId;
use MongoDB\Collection;
use Mongolid\Connection\Connection;
use Mongolid\Container\Ioc;
use Mongolid\Cursor\CacheableCursor;
use Mongolid\Cursor\Cursor;
use Mongolid\Cursor\CursorInterface;
use Mongolid\Event\EventTriggerService;
use Mongolid\Exception\ModelNotFoundException;
use Mongolid\Model\HasAttributesInterface;
use Mongolid\Schema\AbstractSchema;
use Mongolid\Schema\HasSchemaInterface;
use Mongolid\Util\ObjectIdUtils;

/**
 * The DataMapper class will abstract how an Entity is persisted and retrieved
 * from the database.
 * The DataMapper will always use a Schema trough the SchemaMapper to parse the
 * document in and out of the database.
 */
class DataMapper implements HasSchemaInterface
{
    /**
     * Name of the schema class to be used.
     *
     * @var string
     */
    public $schemaClass = AbstractSchema::class;

    /**
     * Schema object. Will be set after the $schemaClass.
     *
     * @var AbstractSchema
     */
    protected $schema;

    /**
     * Connection that is going to be used to interact with the database.
     *
     * @var Connection
     */
    protected $connection;

    /**
     * Have the responsibility of assembling the data coming from the database into actual entities.
     *
     * @var EntityAssembler
     */
    protected $assembler;

    /**
     * In order to dispatch events when necessary.
     *
     * @var EventTriggerService
     */
    protected $eventService;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Upserts the given object into database. Returns success if write concern
     * is acknowledged.
     *
     * Notice: Saves with Unacknowledged WriteConcern will not fire `saved` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function save($entity, array $options = []): bool
    {
        // If the "saving" event returns false we'll bail out of the save and return
        // false, indicating that the save failed. This gives an opportunities to
        // listeners to cancel save operations if validations fail or whatever.
        if (!$this->fireEvent('saving', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->replaceOne(
            ['_id' => $data['_id']],
            $data,
            $this->mergeOptions($options, ['upsert' => true])
        );

        $result = $queryResult->isAcknowledged() &&
            ($queryResult->getModifiedCount() || $queryResult->getUpsertedCount());

        if ($result) {
            $this->afterSuccess($entity);

            $this->fireEvent('saved', $entity);
        }

        return $result;
    }

    /**
     * Inserts the given object into database. Returns success if write concern
     * is acknowledged. Since it's an insert, it will fail if the _id already
     * exists.
     *
     * Notice: Inserts with Unacknowledged WriteConcern will not fire `inserted` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity     the entity used in the operation
     * @param array $options    possible options to send to mongo driver
     * @param bool  $fireEvents whether events should be fired
     */
    public function insert($entity, array $options = [], bool $fireEvents = true): bool
    {
        if ($fireEvents && !$this->fireEvent('inserting', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->insertOne(
            $data,
            $this->mergeOptions($options)
        );

        $result = $queryResult->isAcknowledged() && $queryResult->getInsertedCount();

        if ($result) {
            $this->afterSuccess($entity);

            if ($fireEvents) {
                $this->fireEvent('inserted', $entity);
            }
        }

        return $result;
    }

    /**
     * Updates the given object into database. Returns success if write concern
     * is acknowledged. Since it's an update, it will fail if the document with
     * the given _id did not exists.
     *
     * Notice: Updates with Unacknowledged WriteConcern will not fire `updated` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function update($entity, array $options = []): bool
    {
        if (!$this->fireEvent('updating', $entity, true)) {
            return false;
        }

        if (!$entity->_id) {
            if ($result = $this->insert($entity, $options, false)) {
                $this->afterSuccess($entity);

                $this->fireEvent('updated', $entity);
            }

            return $result;
        }

        $data = $this->parseToDocument($entity);

        $updateData = $this->getUpdateData($entity, $data);

        $queryResult = $this->getCollection()->updateOne(
            ['_id' => $data['_id']],
            $updateData,
            $this->mergeOptions($options)
        );

        $result = $queryResult->isAcknowledged() && $queryResult->getModifiedCount();

        if ($result) {
            $this->afterSuccess($entity);

            $this->fireEvent('updated', $entity);
        }

        return $result;
    }

    /**
     * Removes the given document from the collection.
     *
     * Notice: Deletes with Unacknowledged WriteConcern will not fire `deleted` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function delete($entity, array $options = []): bool
    {
        if (!$this->fireEvent('deleting', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->deleteOne(
            ['_id' => $data['_id']],
            $this->mergeOptions($options)
        );<commit_msg>Fix regression on DataMapper Events<commit_after><?php
namespace Mongolid\DataMapper;

use InvalidArgumentException;
use MongoDB\BSON\ObjectId;
use MongoDB\Collection;
use Mongolid\Connection\Connection;
use Mongolid\Container\Ioc;
use Mongolid\Cursor\CacheableCursor;
use Mongolid\Cursor\Cursor;
use Mongolid\Cursor\CursorInterface;
use Mongolid\Event\EventTriggerService;
use Mongolid\Exception\ModelNotFoundException;
use Mongolid\Model\HasAttributesInterface;
use Mongolid\Schema\AbstractSchema;
use Mongolid\Schema\HasSchemaInterface;
use Mongolid\Util\ObjectIdUtils;

/**
 * The DataMapper class will abstract how an Entity is persisted and retrieved
 * from the database.
 * The DataMapper will always use a Schema trough the SchemaMapper to parse the
 * document in and out of the database.
 */
class DataMapper implements HasSchemaInterface
{
    /**
     * Name of the schema class to be used.
     *
     * @var string
     */
    public $schemaClass = AbstractSchema::class;

    /**
     * Schema object. Will be set after the $schemaClass.
     *
     * @var AbstractSchema
     */
    protected $schema;

    /**
     * Connection that is going to be used to interact with the database.
     *
     * @var Connection
     */
    protected $connection;

    /**
     * Have the responsibility of assembling the data coming from the database into actual entities.
     *
     * @var EntityAssembler
     */
    protected $assembler;

    /**
     * In order to dispatch events when necessary.
     *
     * @var EventTriggerService
     */
    protected $eventService;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Upserts the given object into database. Returns success if write concern
     * is acknowledged.
     *
     * Notice: Saves with Unacknowledged WriteConcern will not fire `saved` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function save($entity, array $options = []): bool
    {
        // If the "saving" event returns false we'll bail out of the save and return
        // false, indicating that the save failed. This gives an opportunities to
        // listeners to cancel save operations if validations fail or whatever.
        if (false === $this->fireEvent('saving', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->replaceOne(
            ['_id' => $data['_id']],
            $data,
            $this->mergeOptions($options, ['upsert' => true])
        );

        $result = $queryResult->isAcknowledged() &&
            ($queryResult->getModifiedCount() || $queryResult->getUpsertedCount());

        if ($result) {
            $this->afterSuccess($entity);

            $this->fireEvent('saved', $entity);
        }

        return $result;
    }

    /**
     * Inserts the given object into database. Returns success if write concern
     * is acknowledged. Since it's an insert, it will fail if the _id already
     * exists.
     *
     * Notice: Inserts with Unacknowledged WriteConcern will not fire `inserted` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity     the entity used in the operation
     * @param array $options    possible options to send to mongo driver
     * @param bool  $fireEvents whether events should be fired
     */
    public function insert($entity, array $options = [], bool $fireEvents = true): bool
    {
        if ($fireEvents && false === $this->fireEvent('inserting', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->insertOne(
            $data,
            $this->mergeOptions($options)
        );

        $result = $queryResult->isAcknowledged() && $queryResult->getInsertedCount();

        if ($result) {
            $this->afterSuccess($entity);

            if ($fireEvents) {
                $this->fireEvent('inserted', $entity);
            }
        }

        return $result;
    }

    /**
     * Updates the given object into database. Returns success if write concern
     * is acknowledged. Since it's an update, it will fail if the document with
     * the given _id did not exists.
     *
     * Notice: Updates with Unacknowledged WriteConcern will not fire `updated` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function update($entity, array $options = []): bool
    {
        if (false === $this->fireEvent('updating', $entity, true)) {
            return false;
        }

        if (!$entity->_id) {
            if ($result = $this->insert($entity, $options, false)) {
                $this->afterSuccess($entity);

                $this->fireEvent('updated', $entity);
            }

            return $result;
        }

        $data = $this->parseToDocument($entity);

        $updateData = $this->getUpdateData($entity, $data);

        $queryResult = $this->getCollection()->updateOne(
            ['_id' => $data['_id']],
            $updateData,
            $this->mergeOptions($options)
        );

        $result = $queryResult->isAcknowledged() && $queryResult->getModifiedCount();

        if ($result) {
            $this->afterSuccess($entity);

            $this->fireEvent('updated', $entity);
        }

        return $result;
    }

    /**
     * Removes the given document from the collection.
     *
     * Notice: Deletes with Unacknowledged WriteConcern will not fire `deleted` event.
     * Return is always false if write concern is Unacknowledged.
     *
     * @param mixed $entity  the entity used in the operation
     * @param array $options possible options to send to mongo driver
     */
    public function delete($entity, array $options = []): bool
    {
        if (false === $this->fireEvent('deleting', $entity, true)) {
            return false;
        }

        $data = $this->parseToDocument($entity);

        $queryResult = $this->getCollection()->deleteOne(
            ['_id' => $data['_id']],
            $this->mergeOptions($options)
        );
{'fraction_non_alphanumeric': 0.10922816207055924, 'fraction_numerical': 0.0, 'mean_word_length': 2.3695821437840645, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3491276', 'n_tokens_mistral': 3497, 'n_tokens_neox': 3360, 'n_words': 1102}