Skip to content

Commit

Permalink
Expose EntityPersister::count() through EntityRepository::count()
Browse files Browse the repository at this point in the history
  • Loading branch information
phansys committed Sep 5, 2016
1 parent 31a0c02 commit f384bb4
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 56 deletions.
54 changes: 31 additions & 23 deletions docs/en/reference/working-with-objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ headline "Hello World" with the ID 1234:
<?php
$article = $entityManager->find('CMS\Article', 1234);
$article->setHeadline('Hello World dude!');
$article2 = $entityManager->find('CMS\Article', 1234);
echo $article2->getHeadline();
Expand Down Expand Up @@ -93,25 +93,25 @@ from newly opened EntityManager.
{
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @Column(type="string") */
private $headline;
/** @ManyToOne(targetEntity="User") */
private $author;
/** @OneToMany(targetEntity="Comment", mappedBy="article") */
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getAuthor() { return $this->author; }
public function getComments() { return $this->comments; }
}
$article = $em->find('Article', 1);
This code only retrieves the ``Article`` instance with id 1 executing
Expand All @@ -132,22 +132,22 @@ your code. See the following code:
<?php
$article = $em->find('Article', 1);
// accessing a method of the user instance triggers the lazy-load
echo "Author: " . $article->getAuthor()->getName() . "\n";
// Lazy Loading Proxies pass instanceof tests:
if ($article->getAuthor() instanceof User) {
// a User Proxy is a generated "UserProxy" class
}
// accessing the comments as an iterator triggers the lazy-load
// retrieving ALL the comments of this article from the database
// using a single SELECT statement
foreach ($article->getComments() as $comment) {
echo $comment->getText() . "\n\n";
}
// Article::$comments passes instanceof tests for the Collection interface
// But it will NOT pass for the ArrayCollection interface
if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
Expand All @@ -167,7 +167,7 @@ methods along the lines of the ``getName()`` method shown below:
{
// lazy loading code
}
public function getName()
{
$this->_load();
Expand Down Expand Up @@ -262,7 +262,7 @@ which means that its persistent state will be deleted once
for and appear in query and collection results. See
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
for more information.


Example:

Expand Down Expand Up @@ -681,13 +681,13 @@ methods on a repository as follows:
<?php
// $em instanceof EntityManager
// All users that are 20 years old
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
// All users that are 20 years old and have a surname of 'Miller'
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
Expand Down Expand Up @@ -723,10 +723,18 @@ examples are equivalent:
<?php
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
// A single user by its nickname (__call magic)
$user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');
Additionally, you can just count the result of the provided conditions when you don't really need the data:

.. code-block:: php
<?php
// Check there is no user with nickname
$availableNickname = 0 === $em->getRepository('MyProject\Domain\User')->count(array('nickname' => 'nonexistent'));
By Criteria
~~~~~~~~~~~

Expand Down Expand Up @@ -774,7 +782,7 @@ A DQL query is represented by an instance of the
<?php
// $em instanceof EntityManager
// All users with an age between 20 and 30 (inclusive).
$q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
$users = $q->getResult();
Expand Down Expand Up @@ -817,18 +825,18 @@ in a central location.
<?php
namespace MyDomain\Model;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
class User
{
}
class UserRepository extends EntityRepository
{
public function getAllAdminUsers()
Expand All @@ -844,7 +852,7 @@ You can access your repository now by calling:
<?php
// $em instanceof EntityManager
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
62 changes: 36 additions & 26 deletions docs/en/tutorials/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Install Doctrine using the Composer Dependency Management tool, by calling:
$ composer install

This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM,
Symfony YAML and Symfony Console into the `vendor` directory. The Symfony
Symfony YAML and Symfony Console into the `vendor` directory. The Symfony
dependencies are not required by Doctrine but will be used in this tutorial.

Add the following directories:
Expand Down Expand Up @@ -131,22 +131,22 @@ step:
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
require_once "vendor/autoload.php";
// Create a simple "default" Doctrine ORM configuration for Annotations
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode);
// or if you prefer yaml or XML
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// database configuration parameters
$conn = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db.sqlite',
);
// obtaining the entity manager
$entityManager = EntityManager::create($conn, $config);
Expand Down Expand Up @@ -185,7 +185,7 @@ doctrine command. Its a fairly simple file:
<?php
// cli-config.php
require_once "bootstrap.php";
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
You can then change into your project directory and call the
Expand All @@ -196,11 +196,11 @@ Doctrine command-line tool:
$ cd project/
$ vendor/bin/doctrine orm:schema-tool:create

At this point no entity metadata exists in `src` so you will see a message like
"No Metadata Classes to process." Don't worry, we'll create a Product entity and
At this point no entity metadata exists in `src` so you will see a message like
"No Metadata Classes to process." Don't worry, we'll create a Product entity and
corresponding metadata in the next section.

You should be aware that during the development process you'll periodically need
You should be aware that during the development process you'll periodically need
to update your database schema to be in sync with your Entities metadata.

You can easily recreate the database:
Expand Down Expand Up @@ -257,15 +257,15 @@ entity definition:
}
}
Note that all fields are set to protected (not public) with a
mutator (getter and setter) defined for every field except $id.
The use of mutators allows Doctrine to hook into calls which
manipulate the entities in ways that it could not if you just
Note that all fields are set to protected (not public) with a
mutator (getter and setter) defined for every field except $id.
The use of mutators allows Doctrine to hook into calls which
manipulate the entities in ways that it could not if you just
directly set the values with ``entity#field = foo;``

The id field has no setter since, generally speaking, your code
should not set this value since it represents a database id value.
(Note that Doctrine itself can still set the value using the
The id field has no setter since, generally speaking, your code
should not set this value since it represents a database id value.
(Note that Doctrine itself can still set the value using the
Reflection API instead of a defined setter function)

The next step for persistence with Doctrine is to describe the
Expand Down Expand Up @@ -567,13 +567,13 @@ change dates. Next we will model the dynamic relationships between the entities
by defining the references between entities.

References between objects are foreign keys in the database. You never have to
(and never should) work with the foreign keys directly, only with the objects
(and never should) work with the foreign keys directly, only with the objects
that represent the foreign key through their own identity.

For every foreign key you either have a Doctrine ManyToOne or OneToOne
association. On the inverse sides of these foreign keys you can have
OneToMany associations. Obviously you can have ManyToMany associations
that connect two tables with each other through a join table with
that connect two tables with each other through a join table with
two foreign keys.

Now that you know the basics about references in Doctrine, we can extend the
Expand Down Expand Up @@ -783,7 +783,7 @@ the database that points from Bugs to Products.
}
We are now finished with the domain model given the requirements.
Lets add metadata mappings for the ``User`` and ``Bug`` as we did for
Lets add metadata mappings for the ``User`` and ``Bug`` as we did for
the ``Product`` before:

.. configuration-block::
Expand Down Expand Up @@ -886,8 +886,8 @@ the ``Product`` before:
Here we have the entity, id and primitive type definitions.
For the "created" field we have used the ``datetime`` type,
which translates the YYYY-mm-dd HH:mm:ss database format
For the "created" field we have used the ``datetime`` type,
which translates the YYYY-mm-dd HH:mm:ss database format
into a PHP DateTime instance and back.

After the field definitions the two qualified references to the
Expand Down Expand Up @@ -1164,10 +1164,10 @@ The console output of this script is then:


As a last resort you can still use Native SQL and a description of the
result set to retrieve entities from the database. DQL boils down to a
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
Native SQL you could even use stored procedures for data retrieval, or
make use of advanced non-portable database queries like PostgreSql's
result set to retrieve entities from the database. DQL boils down to a
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
Native SQL you could even use stored procedures for data retrieval, or
make use of advanced non-portable database queries like PostgreSql's
recursive queries.


Expand All @@ -1180,7 +1180,7 @@ objects only from Doctrine however. For a simple list view like the
previous one we only need read access to our entities and can
switch the hydration from objects to simple PHP arrays instead.

Hydration can be an expensive process so only retrieving what you need can
Hydration can be an expensive process so only retrieving what you need can
yield considerable performance benefits for read-only requests.

Implementing the same list view as before using array hydration we
Expand Down Expand Up @@ -1535,6 +1535,16 @@ As an example here is the code of the first use case "List of Bugs":
Using EntityRepositories you can avoid coupling your model with specific query logic.
You can also re-use query logic easily throughout your application.

The method ``count()`` takes an array of fields or association keys and the values to match against.
This provides you with a convenient and lightweight way to count a resultset when you don't need to
deal with it:

.. code-block:: php
<?php
$productCount = $entityManager->getRepository('Product')
->count(array('name' => $productName));
Conclusion
----------

Expand Down
35 changes: 29 additions & 6 deletions lib/Doctrine/ORM/EntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,16 +196,32 @@ public function findOneBy(array $criteria, array $orderBy = null)
}

/**
* Adds support for magic finders.
* Counts entities by a set of criteria.
*
* @todo Add this method to `ObjectRepository` interface in the next major release
*
* @param array $criteria
*
* @return int The quantity of objects that matches the criteria.
*/
public function count(array $criteria)
{
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);

return $persister->count($criteria);
}

/**
* Adds support for magic finders/counters.
*
* @param string $method
* @param array $arguments
*
* @return array|object The found entity/entities.
* @return array|object|int The found entity/entities or its resulting count.
*
* @throws ORMException
* @throws \BadMethodCallException If the method called is an invalid find* method
* or no find* method at all and therefore an invalid
* @throws \BadMethodCallException If the method called is an invalid find* or countBy method
* or no find* or countBy method at all and therefore an invalid
* method call.
*/
public function __call($method, $arguments)
Expand All @@ -221,21 +237,28 @@ public function __call($method, $arguments)
$method = 'findOneBy';
break;

case (0 === strpos($method, 'countBy')):
$by = substr($method, 7);
$method = 'count';
break;

default:
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
}

if (empty($arguments)) {
$argsCount = count($arguments);

if (0 === $argsCount) {
throw ORMException::findByRequiresParameter($method . $by);
}

$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));

if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
switch (count($arguments)) {
switch ($argsCount) {
case 1:
return $this->$method(array($fieldName => $arguments[0]));

Expand Down
Loading

0 comments on commit f384bb4

Please sign in to comment.