Herkese merhaba!

Uzun yıllardır bol miktarda kişisel zaman ve enerji harcayarak bilgimizi hepinizle paylaşıyoruz. Ancak şu andan itibaren bu blogu çalışır durumda tutabilmek için yardımınıza ihtiyacımız var. Yapmanız gereken tek şey, sitedeki reklamlardan birine tıklamak olacaktır, aksi takdirde hosting vb. masraflar nedeniyle maalesef yayından kaldırılacaktır. Teşekkürler.

Many-to-many ilişkilerde, ilişki anahtarlarını tutmak için ek bir tane entity yaratılır. Sonuç olarak elimizde iki tane one-to-many ilişki örneği oluşur ve bunlar, o yaratılan ek entitye giderler. Örneğimiz Student ve Subject senaryosu üzerine kuruludur.


Routing


# sport/app/config/routing.yml
football_frontend:
resource: "@FootballFrontendBundle/Controller"
prefix: /
type: annotation

Entityler


Student


# src/Football/FrontendBundle/Entity/Student.php
namespace Football\FrontendBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
* @ORM\Entity(repositoryClass="Football\FrontendBundle\Repository\StudentRepository")
* @ORM\Table(name="student")
* @UniqueEntity(fields="student_id", message="StudentID is already taken.")
*/
class Student
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="smallint")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @var string
*
* @ORM\Column(name="student_id", type="string", length=15, unique=true)
*/
protected $studentId;

/**
* @var object
*
* @ORM\OneToMany(
* targetEntity="StudentSubject",
* mappedBy="studentMap",
* cascade={"persist", "remove"}
* )
*/
protected $studentInverse;

/**
* Constructor
*/
public function __construct()
{
$this->studentInverse = new ArrayCollection();
}

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set studentId
*
* @param string $studentId
* @return Student
*/
public function setStudentId($studentId)
{
$this->studentId = $studentId;

return $this;
}

/**
* Get studentId
*
* @return string
*/
public function getStudentId()
{
return $this->studentId;
}

/**
* Add studentInverse
*
* @param StudentSubject $studentInverse
* @return Student
*/
public function addStudentInverse(StudentSubject $studentInverse)
{
$this->studentInverse[] = $studentInverse;

return $this;
}

/**
* Remove studentInverse
*
* @param StudentSubject $studentInverse
*/
public function removeStudentInverse(StudentSubject $studentInverse)
{
$this->studentInverse->removeElement($studentInverse);
}

/**
* Get studentInverse
*
* @return Collection
*/
public function getStudentInverse()
{
return $this->studentInverse;
}
}

Subject


# src/Football/FrontendBundle/Entity/Subject.php
namespace Football\FrontendBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
* @ORM\Entity(repositoryClass="Football\FrontendBundle\Repository\SubjectRepository")
* @ORM\Table(name="subject")
* @UniqueEntity(fields="subject_code", message="Subject code is already taken.")
*/
class Subject
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="smallint")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @var string
*
* @ORM\Column(name="code", type="string", length=5, unique=true)
*/
protected $code;

/**
* @var object
*
* @ORM\OneToMany(
* targetEntity="StudentSubject",
* mappedBy="subjectMap",
* cascade={"persist", "remove"}
* )
*/
protected $subjectInverse;

/**
* Constructor
*/
public function __construct()
{
$this->subjectInverse = new ArrayCollection();
}

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set studentId
*
* @param string $code
* @return Subject
*/
public function setCode($code)
{
$this->code = $code;

return $this;
}

/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}

/**
* Add subjectInverse
*
* @param StudentSubject $subjectInverse
* @return Subject
*/
public function addSubjectInverse(StudentSubject $subjectInverse)
{
$this->subjectInverse[] = $subjectInverse;

return $this;
}

/**
* Remove subjectInverse
*
* @param StudentSubject $subjectInverse
*/
public function removeSubjectInverse(StudentSubject $subjectInverse)
{
$this->subjectInverse->removeElement($subjectInverse);
}

/**
* Get subjectInverse
*
* @return Collection
*/
public function getSubjectInverse()
{
return $this->subjectInverse;
}
}

StudentSubject


# src/Football/FrontendBundle/Entity/StudentSubject.php
namespace Football\FrontendBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
* @ORM\Entity(repositoryClass="Football\FrontendBundle\Repository\StudentSubjectRepository")
* @ORM\Table(name="student_subject", uniqueConstraints={@ORM\UniqueConstraint(columns={"student", "subject"})})
* @UniqueEntity(fields={"studentMap","subjectMap"}, message="Selected combination exists in database.")
*/
class StudentSubject
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @var object
*
* @ORM\ManyToOne(targetEntity="Student", inversedBy="studentInverse")
* @ORM\JoinColumn(name="student", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $studentMap;

/**
* @var object
*
* @ORM\ManyToOne(targetEntity="Subject", inversedBy="subjectInverse")
* @ORM\JoinColumn(name="subject", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $subjectMap;

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set studentMap
*
* @param Student $studentMap
* @return StudentSubject
*/
public function setStudentMap(Student $studentMap)
{
$this->studentMap = $studentMap;

return $this;
}

/**
* Get studentMap
*
* @return Student
*/
public function getStudentMap()
{
return $this->studentMap;
}

/**
* Set subjectMap
*
* @param Subject $subjectMap
* @return StudentSubject
*/
public function setSubjectMap(Subject $subjectMap)
{
$this->subjectMap = $subjectMap;

return $this;
}

/**
* Get subjectMap
*
* @return Subject
*/
public function getSubjectMap()
{
return $this->subjectMap;
}
}

Repositoryler


Student


# sport/src/Football/FrontendBundle/Repository/StudentRepository.php
namespace Football\FrontendBundle\Repository;

use Doctrine\ORM\EntityRepository;

class StudentRepository extends EntityRepository
{
}

Subject


# sport/src/Football/FrontendBundle/Repository/SubjectRepository.php
namespace Football\FrontendBundle\Repository;

use Doctrine\ORM\EntityRepository;

class SubjectRepository extends EntityRepository
{
}

StudentSubject


# sport/src/Football/FrontendBundle/Repository/StudentSubjectRepository.php
namespace Football\FrontendBundle\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;

class StudentSubjectRepository extends EntityRepository
{
public function findAll()
{
$fields = [
'st.id AS stId',
'st.studentId AS stStId',
'sb.id AS sbId',
'sb.code AS sbCode',
];

return
$this
->createQueryBuilder('ss')
->select($fields)
->join('ss.studentMap', 'st')
->join('ss.subjectMap', 'sb')
->addOrderBy('st.studentId', 'ASC')
->addOrderBy('sb.code', 'ASC')
->getQuery()
->getResult(Query::HYDRATE_SCALAR);
}
}

FormTypelar


Student


Bunu kullanmayacağız ama yinede örnek olsun diye görelim.


# sport/src/Football/FrontendBundle/Form/Type/StudentType.php
namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;

class StudentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add(
'studentId',
'text',
[
'error_bubbling' => true,
'constraints' => [
new NotBlank(
[
'message' => 'StudentID is required.'
]
),
new Length(
[
'max' => 15,
'maxMessage' => 'StudentID cannot be longer than {{ limit }} characters.'
]
)
]
]
);
}

public function getName()
{
return 'student';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\Student']
);
}
}

Subject


Bunu kullanmayacağız ama yinede örnek olsun diye görelim.


# sport/src/Football/FrontendBundle/Form/Type/SubjectType.php
namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;

class SubjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add(
'code',
'text',
[
'error_bubbling' => true,
'constraints' => [
new NotBlank(
[
'message' => 'Code is required.'
]
),
new Length(
[
'max' => 5,
'maxMessage' => 'Code cannot be longer than {{ limit }} characters.'
]
)
]
]
);
}

public function getName()
{
return 'subject';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\Subject']
);
}
}

StudentSubject


# sport/src/Football/FrontendBundle/Form/Type/StudentSubjectType.php
namespace Football\FrontendBundle\Form\Type;

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

class StudentSubjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add(
'studentMap',
'entity',
[
'error_bubbling' => true,
'class' => 'FootballFrontendBundle:Student',
'property' => 'studentId',
'multiple' => false,
'expanded' => false,
'empty_value' => '',
'query_builder' => function (EntityRepository $repo)
{
return $repo->createQueryBuilder('st')->orderBy('st.studentId', 'ASC');
},
'constraints' => [
new NotBlank(
[
'message' => 'Student is required.'
]
)
]
]
)
->add(
'subjectMap',
'entity',
[
'error_bubbling' => true,
'class' => 'FootballFrontendBundle:Subject',
'property' => 'code',
'multiple' => false,
'expanded' => false,
'empty_value' => '',
'query_builder' => function (EntityRepository $repo)
{
return $repo->createQueryBuilder('sb')->orderBy('sb.code', 'ASC');
},
'constraints' => [
new NotBlank(
[
'message' => 'Subject is required.'
]
)
]
]
);
}

public function getName()
{
return 'studentsubject';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\StudentSubject']
);
}
}

Controllerlar


Student


# sport/src/Football/FrontendBundle/Controller/StudentController.php
namespace Football\FrontendBundle\Controller;

use Football\FrontendBundle\Entity\Student;
use Football\FrontendBundle\Form\Type\StudentType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/student")
*/
class StudentController extends Controller
{
/**
* @Route("")
* @Method({"GET"})
*/
public function indexAction()
{
return $this->getTemplate('index');
}

/**
* @Route("/list")
* @Method({"GET"})
*/
public function listAction()
{
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Student');

return $this->getTemplate(
'list',
[
'students' => $repo->findAll()
]
);
}

/**
* Creates form.
*
* @param Student $entity
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm(Student $entity, $method, $action)
{
return $this->createForm(
new StudentType(),
$entity,
[
'method' => $method,
'action' => $action
]
);
}

/**
* Creates template.
*
* @param string $template
* @param array $parameters
*
* @return Response
*/
private function getTemplate($template, array $parameters = [])
{
return $this->render(
sprintf('FootballFrontendBundle:StudentSubject\Student:%s.html.twig', $template),
$parameters
);
}
}

Subject


# sport/src/Football/FrontendBundle/Controller/SubjectController.php
namespace Football\FrontendBundle\Controller;

use Football\FrontendBundle\Entity\Subject;
use Football\FrontendBundle\Form\Type\SubjectType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/subject")
*/
class SubjectController extends Controller
{
/**
* @Route("")
* @Method({"GET"})
*/
public function indexAction()
{
return $this->getTemplate('index');
}

/**
* @Route("/list")
* @Method({"GET"})
*/
public function listAction()
{
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Subject');

return $this->getTemplate(
'list',
[
'subjects' => $repo->findAll()
]
);
}

/**
* Creates form.
*
* @param Subject $entity
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm(Subject $entity, $method, $action)
{
return $this->createForm(
new SubjectType(),
$entity,
[
'method' => $method,
'action' => $action
]
);
}

/**
* Creates template.
*
* @param string $template
* @param array $parameters
*
* @return Response
*/
private function getTemplate($template, array $parameters = [])
{
return $this->render(
sprintf('FootballFrontendBundle:StudentSubject\Subject:%s.html.twig', $template),
$parameters
);
}
}

StudentSubject


# sport/src/Football/FrontendBundle/Controller/StudentSubjectController.php
namespace Football\FrontendBundle\Controller;

use Doctrine\DBAL\DBALException;
use Doctrine\ORM\ORMException;
use Exception;
use Football\FrontendBundle\Entity\StudentSubject;
use Football\FrontendBundle\Form\Type\StudentSubjectType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/studentsubject")
*/
class StudentSubjectController extends Controller
{
const ROUTER_PREFIX = 'football_frontend_studentsubject_';

/**
* @Route("")
* @Method({"GET"})
*/
public function indexAction()
{
return $this->getTemplate('index');
}

/**
* @Route("/list")
* @Method({"GET"})
*/
public function listAction()
{
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:StudentSubject');

return $this->getTemplate(
'list',
[
'studentsubjects' => $repo->findAll()
]
);
}

/**
* @Route("/create")
* @Method({"GET"})
*/
public function createAction()
{
$form = $this->getForm(
new StudentSubject(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);

return $this->getTemplate(
'create',
[
'form' => $form->createView()
]
);
}

/**
* @param Request $request
*
* @Route("/create")
* @Method({"POST"})
*
* @return RedirectResponse|Response
* @throws Exception
*/
public function createProcessAction(Request $request)
{
if ($request->getMethod() != 'POST') {
throw new Exception('StudentSubject create: only POST method is allowed.');
}

$form = $this->getForm(
new StudentSubject(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);
$form->handleRequest($request);

if (!$form->isSubmitted()) {
throw new Exception('StudentSubject create: form is not submitted.');
}

if ($form->isValid() !== true) {
return $this->getTemplate(
'create',
[
'form' => $form->createView()
]
);
}

try {
$data = $form->getData();

$league = new StudentSubject();
$league->setStudentMap($data->getStudentMap());
$league->setSubjectMap($data->getSubjectMap());

$em = $this->getDoctrine()->getManager();
$em->persist($league);
$em->flush();
} catch (DBALException $e) {
$message = sprintf('DBALException [%s]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%s]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%s]: %s', $e->getCode(), $e->getMessage());
}

if (isset($message)) {
throw new Exception($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}

/**
* Creates form.
*
* @param StudentSubject $entity
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm(StudentSubject $entity, $method, $action)
{
return $this->createForm(
new StudentSubjectType(),
$entity,
[
'method' => $method,
'action' => $action
]
);
}

/**
* Creates template.
*
* @param string $template
* @param array $parameters
*
* @return Response
*/
private function getTemplate($template, array $parameters = [])
{
return $this->render(
sprintf('FootballFrontendBundle:StudentSubject:%s.html.twig', $template),
$parameters
);
}
}

Twig şablonları


Student


# sport/src/Football/FrontendBundle/Resources/views/StudentSubject/Student/list.html.twig
{% extends 'FootballFrontendBundle:StudentSubject/Student:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
STUDENT - List
<hr />
{% if students is defined and students|length != 0 %}
<table border="1px">
<tr>
<td>#</td>
<td>ID</td>
<td>STUDENT ID</td>
</tr>
{% for student in students %}
<tr>
<td class="head">{{ loop.index }}</td>
<td>{{ student.id }}</td>
<td>{{ student.studentId }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No student found in database!
{% endif %}
{% endspaceless %}
{% endblock %}

Subject


# sport/src/Football/FrontendBundle/Resources/views/StudentSubject/Subject/list.html.twig
{% extends 'FootballFrontendBundle:StudentSubject/Subject:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
SUBJECT - List
<hr />
{% if subjects is defined and subjects|length != 0 %}
<table border="1px">
<tr>
<td>#</td>
<td>ID</td>
<td>CODE</td>
</tr>
{% for subject in subjects %}
<tr>
<td class="head">{{ loop.index }}</td>
<td>{{ subject.id }}</td>
<td>{{ subject.code }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No subject found in database!
{% endif %}
{% endspaceless %}
{% endblock %}

StudentSubject


# sport/src/Football/FrontendBundle/Resources/views/StudentSubject/list.html.twig
{% extends 'FootballFrontendBundle:StudentSubject:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
STUDENT SUBJECT - List
<hr />
{% if studentsubjects is defined and studentsubjects|length != 0 %}
<table border="1px">
<tr>
<td>#</td>
<td>STUDENT ID</td>
<td>STUDENT STUDENT_ID</td>
<td>SUBJECT ID</td>
<td>SUBJECT CODE</td>
</tr>
{% for studentsubject in studentsubjects %}
<tr>
<td class="head">{{ loop.index }}</td>
<td>{{ studentsubject.stId }}</td>
<td>{{ studentsubject.stStId }}</td>
<td>{{ studentsubject.sbId }}</td>
<td>{{ studentsubject.sbCode }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No student_subject found in database!
{% endif %}
{% endspaceless %}
{% endblock %}

# sport/src/Football/FrontendBundle/Resources/views/StudentSubject/create.html.twig
{% extends 'FootballFrontendBundle:StudentSubject:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
STUDENT COUNTRY - Create
<hr />
{{ form_start(form, { attr: {novalidate: 'novalidate'} }) }}
{% if form_errors(form) != '' %}

{{ form_errors(form) }}
{% endif %}

<p>STUDENT: {{ form_widget(form.studentMap) }}</p>
<p>SUBJECT: {{ form_widget(form.subjectMap) }}</p>

<p><button name="button">Submit</button></p>
{{ form_end(form) }}
{% endspaceless %}
{% endblock %}