Bazen isteğe cevap vermek için, veritabanından aldığımız bilgiyi model classlar ile eşleştirip, cevap olarak geri göndeririz. Eğer model class içinde gereğinden fazla alan olursa, bu işlemi manuel olarak yaparken başımız biraz ağrıyabilir. Bu gibi sıkıntılardan kurtulmak için BCCAutoMapperBundle kullanıp, tüm eşleştirme işlemini otomatik hale dönüştürebiliriz.

Aşağıdaki örneğimizde elimizde üç tane entity var ve bunlar birbirleriyle one-to-many şeklinde bağlıdırlar, League (1 to n) Team (1 to n) Player. İsteğe vereceğimiz cevapta, League model hem kendi alanlarını, hem de Team modelini kapsayacak. Bununla birlikte, Team model ise, hem kendi alanlarını, hem de Player modelini kapsayacak.


Kurulum


Composer ile bcc/auto-mapper-bundle paketini kurun ve new BCC\AutoMapperBundle\BCCAutoMapperBundle() satırını AppKernel.php dosyasına ekleyin.


FootballController


namespace Application\FrontendBundle\Controller;

use Application\FrontendBundle\Service\FootballServiceInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;

/**
* @Route("football", service="application_frontend.controller.football")
*/
class FootballController extends Controller
{
private $footballService;

public function __construct(
FootballServiceInterface $footballService
) {
$this->footballService = $footballService;
}

/**
* @Route("")
* @Method({"GET"})
*/
public function indexAction()
{
$league = $this->footballService->getAll();

return new Response(json_encode($league));
}
}

Controllers.yml


services:
application_frontend.controller.football:
class: Application\FrontendBundle\Controller\FootballController
arguments:
- @application_frontend.service.football

Entitiler


League


namespace Application\FrontendBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass="Application\FrontendBundle\Repository\LeagueRepository")
* @ORM\Table(name="league")
*/
class League
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string
*
* @ORM\Column(name="name", type="string")
*/
private $name;

/**
* @var string
*
* @ORM\Column(name="country", type="string")
*/
private $country;

/**
* @ORM\OneToMany(targetEntity="Team", mappedBy="league", cascade={"persist", "remove"})
*/
private $team;

......
......
......
}

Team


namespace Application\FrontendBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="team")
*/
class Team
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string
*
* @ORM\Column(name="name", type="string")
*/
private $name;

/**
* @var string
*
* @ORM\Column(name="colours", type="json_array")
*/
private $colours;

/**
* @ORM\ManyToOne(targetEntity="League", inversedBy="team")
* @ORM\JoinColumn(name="league_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
*/
private $league;

/**
* @ORM\OneToMany(targetEntity="Player", mappedBy="team", cascade={"persist", "remove"})
*/
private $player;

......
......
......
}

Player


namespace Application\FrontendBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="player")
*/
class Player
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string
*
* @ORM\Column(name="name", type="string")
*/
private $name;

/**
* @ORM\ManyToOne(targetEntity="Team", inversedBy="player")
* @ORM\JoinColumn(name="team_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
*/
private $team;

......
......
......
}

Modeller


League


namespace Application\FrontendBundle\Model;

use JMS\Serializer\Annotation as Serializer;

class League
{
/**
* @var int
*
* @Serializer\Type("integer")
*/
public $id;

/**
* @var string
*
* @Serializer\Type("string")
*/
public $name;

/**
* @var string
*
* @Serializer\Type("string")
*/
public $country;

/**
* @var Team[]
*
* @Serializer\Type("array<Application\FrontendBundle\Model\Team>")
*/
public $teams = [];
}

Team


namespace Application\FrontendBundle\Model;

use JMS\Serializer\Annotation as Serializer;

class Team
{
/**
* @var int
*
* @Serializer\Type("integer")
*/
public $id;

/**
* @var string
*
* @Serializer\Type("string")
*/
public $name;

/**
* @var string
*
* @Serializer\Type("string")
*/
public $colours;

/**
* @var Player[]
*
* @Serializer\Type("array<Application\FrontendBundle\Model\Player>")
*/
public $players = [];
}

Player


namespace Application\FrontendBundle\Model;

use JMS\Serializer\Annotation as Serializer;

class Player
{
/**
* @var int
*
* @Serializer\Type("integer")
*/
public $id;

/**
* @var string
*
* @Serializer\Type("string")
*/
public $name;
}

FootballMapperInterface


Bu kısım önemli! Eğer entity class içindeki alanlar ile, onunla alakalı model class içindeki alan isimlerinde uyuşmazlık var ise, mapper class içinde manuel eşleştirme yapmanız gerekecektir. Bu gibi durumlarda ise bir tane mapper class işinizi görmez. Her model için bir tane mapper class yaratmanız gerekir. Bu örneğimizde böyle bir sorunumuz olmadığı için, aşağıdaki mapper class işimizi görür.


namespace Application\FrontendBundle\Model;

use Application\FrontendBundle\Entity\League as LeagueEntity;
use Application\FrontendBundle\Model\League as LeagueModel;
use Application\FrontendBundle\Entity\Team as TeamEntity;
use Application\FrontendBundle\Model\Team as TeamModel;
use Application\FrontendBundle\Entity\Player as PlayerEntity;
use Application\FrontendBundle\Model\Player as PlayerModel;

interface FootballMapperInterface
{
/**
* @param LeagueEntity|TeamEntity|PlayerEntity $source
* @param LeagueModel|TeamModel|PlayerModel $destination
*
* @return LeagueModel|TeamModel|PlayerModel
*/
public function map($source, $destination);
}

FootballMapper


namespace Application\FrontendBundle\Model;

use BCC\AutoMapperBundle\Mapper\Mapper;

class FootballMapper implements FootballMapperInterface
{
private $mapper;

public function __construct(Mapper $mapper)
{
$this->mapper = $mapper;
}

public function map($source, $destination)
{
$this->mapper->createMap(get_class($source), get_class($destination));

return $this->mapper->map($source, $destination);
}
}

Mappers.yml


services:
application_frontend.mapper.football:
class: Application\FrontendBundle\Model\FootballMapper
arguments:
- @bcc_auto_mapper.mapper

LeagueRepository


namespace Application\FrontendBundle\Repository;

use Doctrine\ORM\EntityRepository;

class LeagueRepository extends EntityRepository
{
public function findAll()
{
return
$this
->createQueryBuilder('l')
->select('l, t, p')
->leftJoin('l.team', 't')
->leftJoin('t.player', 'p')
->getQuery()
->getResult();
}
}

Repositories.yml


services:
application_frontend.repository.league:
class: Application\FrontendBundle\Repository\LeagueRepository
factory: [@doctrine.orm.entity_manager, getRepository]
arguments: [Application\FrontendBundle\Entity\League]

FootballServiceInterface


namespace Application\FrontendBundle\Service;

interface FootballServiceInterface
{
public function getAll();
}

FootballService


namespace Application\FrontendBundle\Service;

use Application\FrontendBundle\Factory\FootballFactoryInterface;
use Application\FrontendBundle\Repository\LeagueRepository;

class FootballService implements FootballServiceInterface
{
private $leagueRepository;
private $footballFactory;

public function __construct(
LeagueRepository $leagueRepository,
FootballFactoryInterface $footballFactory
) {
$this->leagueRepository = $leagueRepository;
$this->footballFactory = $footballFactory;
}

public function getAll()
{
$leagues = $this->leagueRepository->findAll();
$result = $this->footballFactory->createLeagueResult($leagues);

return $result;
}
}

Services.yml


services:
application_frontend.service.football:
class: Application\FrontendBundle\Service\FootballService
arguments:
- @application_frontend.repository.league
- @application_frontend.factory.football

FootballFactoryInterface


namespace Application\FrontendBundle\Factory;

interface FootballFactoryInterface
{
public function createLeagueResult(array $leagues = []);
}

FootballFactory


namespace Application\FrontendBundle\Factory;

use Application\FrontendBundle\Entity\Player as PlayerEntity;
use Application\FrontendBundle\Model\FootballMapperInterface;
use Application\FrontendBundle\Model\Player as PlayerModel;
use Application\FrontendBundle\Entity\Team as TeamEntity;
use Application\FrontendBundle\Model\Team as TeamModel;
use Application\FrontendBundle\Entity\League as LeagueEntity;
use Application\FrontendBundle\Model\League as LeagueModel;

class FootballFactory implements FootballFactoryInterface
{
private $footballMapper;

public function __construct(
FootballMapperInterface $footballMapper
) {
$this->footballMapper = $footballMapper;
}

public function createLeagueResult(array $leagues = [])
{
$result = [];

/** @var LeagueEntity $league */
foreach ($leagues as $league) {
$leagueModel = $this->footballMapper->map($league, new LeagueModel());

/** @var TeamEntity $team */
foreach ($league->getTeam() as $team) {
$teamModel = $this->footballMapper->map($team, new TeamModel());
$leagueModel->teams[] = $teamModel;

/** @var PlayerEntity $player */
foreach ($team->getPlayer() as $player) {
$playerModel = $this->footballMapper->map($player, new PlayerModel());
$teamModel->players[] = $playerModel;
}
}

$result[] = $leagueModel;
}

return $result;
}
}

Factories.yml


services:
application_frontend.factory.football:
class: Application\FrontendBundle\Factory\FootballFactory
arguments:
- @application_frontend.mapper.football

Veritabanı


mysql> SELECT * FROM league;
+----+----------------+----------------+
| id | name | country |
+----+----------------+----------------+
| 1 | Premier League | United Kingdom |
| 2 | Bundesliga | Germany |
+----+----------------+----------------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM team;
+----+-----------+-------------------+------------------------+
| id | league_id | name | colours |
+----+-----------+-------------------+------------------------+
| 1 | 1 | Arsenal | ["red","white"] |
| 2 | 2 | Borussia Dortmund | ["yellow","black"] |
| 3 | 2 | Bayern Munich | ["red","white","navy"] |
| 4 | 2 | Werder Bremen | ["green","white"] |
+----+-----------+-------------------+------------------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM player;
+----+---------+---------------+
| id | team_id | name |
+----+---------+---------------+
| 1 | 1 | Mesut Ozil |
| 2 | 1 | Aaron Ramsey |
| 3 | 2 | Nuri Sahin |
| 4 | 3 | Thomas Muller |
| 5 | 3 | Philipp Lahm |
+----+---------+---------------+
5 rows in set (0.00 sec)

mysql> SELECT
-> league.id AS league_id,
-> league.name AS league_name,
-> league.country AS league_country,
-> team.id AS team_id,
-> team.name AS team_name,
-> team.colours AS team_colours,
-> player.id AS player_id,
-> player.name AS player_name
-> FROM league
-> LEFT JOIN team ON league.id = team.league_id
-> LEFT JOIN player ON team.id = player.team_id;
+-----------+----------------+----------------+---------+-------------------+------------------------+-----------+---------------+
| league_id | league_name | league_country | team_id | team_name | team_colours | player_id | player_name |
+-----------+----------------+----------------+---------+-------------------+------------------------+-----------+---------------+
| 1 | Premier League | United Kingdom | 1 | Arsenal | ["red","white"] | 1 | Mesut Ozil |
| 1 | Premier League | United Kingdom | 1 | Arsenal | ["red","white"] | 2 | Aaron Ramsey |
| 2 | Bundesliga | Germany | 2 | Borussia Dortmund | ["yellow","black"] | 3 | Nuri Sahin |
| 2 | Bundesliga | Germany | 3 | Bayern Munich | ["red","white","navy"] | 4 | Thomas Muller |
| 2 | Bundesliga | Germany | 3 | Bayern Munich | ["red","white","navy"] | 5 | Philipp Lahm |
| 2 | Bundesliga | Germany | 4 | Werder Bremen | ["green","white"] | NULL | NULL |
+-----------+----------------+----------------+---------+-------------------+------------------------+-----------+---------------+
6 rows in set (0.00 sec)

Test


Ulaşacağımız adres: GET http://football.dev/app_dev.php/football


Asıl cevap


{
{
"id": 1,
"name": "Premier League",
"country": "United Kingdom",
"teams": [
{
"id": 1,
"name": "Arsenal",
"colours": [
"red",
"white"
],
"players": [
{
"id": 1,
"name": "Mesut Ozil"
},
{
"id": 2,
"name": "Aaron Ramsey"
}
]
}
]
},
{
"id": 2,
"name": "Bundesliga",
"country": "Germany",
"teams": [
{
"id": 2,
"name": "Borussia Dortmund",
"colours": [
"yellow",
"black"
],
"players": [
{
"id": 3,
"name": "Nuri Sahin"
}
]
},
{
"id": 3,
"name": "Bayern Munich",
"colours": [
"red",
"white",
"navy"
],
"players": [
{
"id": 4,
"name": "Thomas Muller"
},
{
"id": 5,
"name": "Philipp Lahm"
}
]
},
{
"id": 4,
"name": "Werder Bremen",
"colours": [
"green",
"white"
],
"players": null
}
]
}
]

Eşleşleşme sonucu


Array
(
[0] => Application\FrontendBundle\Model\League Object
(
[id] => 1
[name] => Premier League
[country] => United Kingdom
[teams] => Array
(
[0] => Application\FrontendBundle\Model\Team Object
(
[id] => 1
[name] => Arsenal
[colours] => Array
(
[0] => red
[1] => white
)
[players] => Array
(
[0] => Application\FrontendBundle\Model\Player Object
(
[id] => 1
[name] => Mesut Ozil
)
[1] => Application\FrontendBundle\Model\Player Object
(
[id] => 2
[name] => Aaron Ramsey
)
)
)
)
)
[1] => Application\FrontendBundle\Model\League Object
(
[id] => 2
[name] => Bundesliga
[country] => Germany
[teams] => Array
(
[0] => Application\FrontendBundle\Model\Team Object
(
[id] => 2
[name] => Borussia Dortmund
[colours] => Array
(
[0] => yellow
[1] => black
)

[players] => Array
(
[0] => Application\FrontendBundle\Model\Player Object
(
[id] => 3
[name] => Nuri Sahin
)
)
)
[1] => Application\FrontendBundle\Model\Team Object
(
[id] => 3
[name] => Bayern Munich
[colours] => Array
(
[0] => red
[1] => white
[2] => navy
)
[players] => Array
(
[0] => Application\FrontendBundle\Model\Player Object
(
[id] => 4
[name] => Thomas Muller
)
[1] => Application\FrontendBundle\Model\Player Object
(
[id] => 5
[name] => Philipp Lahm
)
)
)
[2] => Application\FrontendBundle\Model\Team Object
(
[id] => 4
[name] => Werder Bremen
[colours] => Array
(
[0] => green
[1] => white
)
[players] =>
)
)
)
)