Symfony exception bij serializen
Ik heb een nieuw Symfony2-project waarbij ik een bestaande database automatisch heb laten mappen naar entiteiten. Ik wil graag een RESTapi gaan maken met dit Symfony-project, dus heb ik de volgende controller:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class APIController extends Controller
{
/**
* @Route("/api/{type}/{id}", name="get")
* @Method("GET")
*/
public function getAction($type, $id, Request $request)
{
$response = null;
$serializer = new Serializer(
array(
new ObjectNormalizer()
),
array(
new XmlEncoder(),
new JsonEncoder()
)
);
$repository = $this->getDoctrine()->getRepository('AppBundle:' . $type);
$entity = $repository->find($id);
$test = $serializer->serialize($entity, 'json');
$response = new Response($serializer->serialize($entity, 'json'));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
?>
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class APIController extends Controller
{
/**
* @Route("/api/{type}/{id}", name="get")
* @Method("GET")
*/
public function getAction($type, $id, Request $request)
{
$response = null;
$serializer = new Serializer(
array(
new ObjectNormalizer()
),
array(
new XmlEncoder(),
new JsonEncoder()
)
);
$repository = $this->getDoctrine()->getRepository('AppBundle:' . $type);
$entity = $repository->find($id);
$test = $serializer->serialize($entity, 'json');
$response = new Response($serializer->serialize($entity, 'json'));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
?>
Ik heb wel een deel weggelaten, het gaat namelijk om het volgende. Als ik nu /api/Ride/1 opvraag, dan krijg ik de volgende exception:
Runtime Notice: Accessing static property Proxies\__CG__\AppBundle\Entity\Route::$lazyPropertiesDefaults as non static
Deze Ride heeft een referentie naar Route. Deze entiteiten zien er zo uit: (getters en setters weggelaten)
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ride
*
* @ORM\Table(name="ride", indexes={@ORM\Index(name="route", columns={"route"})})
* @ORM\Entity
*/
class Ride
{
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=50, nullable=true)
*/
private $description;
/**
* @var boolean
*
* @ORM\Column(name="retour", type="boolean", nullable=true)
*/
private $retour;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @var integer
*
* @ORM\Column(name="ridenumber", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $ridenumber;
/**
* @var \AppBundle\Entity\Route
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Route")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="route", referencedColumnName="routenumber")
* })
*/
private $route;
}
?>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ride
*
* @ORM\Table(name="ride", indexes={@ORM\Index(name="route", columns={"route"})})
* @ORM\Entity
*/
class Ride
{
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=50, nullable=true)
*/
private $description;
/**
* @var boolean
*
* @ORM\Column(name="retour", type="boolean", nullable=true)
*/
private $retour;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @var integer
*
* @ORM\Column(name="ridenumber", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $ridenumber;
/**
* @var \AppBundle\Entity\Route
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Route")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="route", referencedColumnName="routenumber")
* })
*/
private $route;
}
?>
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Route
*
* @ORM\Table(name="route")
* @ORM\Entity
*/
class Route
{
/**
* @var string
*
* @ORM\Column(name="from", type="string", length=50, nullable=false)
*/
private $from;
/**
* @var string
*
* @ORM\Column(name="addressfrom", type="string", length=50, nullable=false)
*/
private $addressfrom;
/**
* @var string
*
* @ORM\Column(name="postalcodefrom", type="string", length=7, nullable=false)
*/
private $postalcodefrom;
/**
* @var string
*
* @ORM\Column(name="to", type="string", length=50, nullable=false)
*/
private $to;
/**
* @var string
*
* @ORM\Column(name="addressto", type="string", length=50, nullable=false)
*/
private $addressto;
/**
* @var string
*
* @ORM\Column(name="postalcodeto", type="string", length=7, nullable=false)
*/
private $postalcodeto;
/**
* @var string
*
* @ORM\Column(name="distance", type="decimal", precision=5, scale=2, nullable=false)
*/
private $distance;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @var integer
*
* @ORM\Column(name="routenumber", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $routenumber;
}
?>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Route
*
* @ORM\Table(name="route")
* @ORM\Entity
*/
class Route
{
/**
* @var string
*
* @ORM\Column(name="from", type="string", length=50, nullable=false)
*/
private $from;
/**
* @var string
*
* @ORM\Column(name="addressfrom", type="string", length=50, nullable=false)
*/
private $addressfrom;
/**
* @var string
*
* @ORM\Column(name="postalcodefrom", type="string", length=7, nullable=false)
*/
private $postalcodefrom;
/**
* @var string
*
* @ORM\Column(name="to", type="string", length=50, nullable=false)
*/
private $to;
/**
* @var string
*
* @ORM\Column(name="addressto", type="string", length=50, nullable=false)
*/
private $addressto;
/**
* @var string
*
* @ORM\Column(name="postalcodeto", type="string", length=7, nullable=false)
*/
private $postalcodeto;
/**
* @var string
*
* @ORM\Column(name="distance", type="decimal", precision=5, scale=2, nullable=false)
*/
private $distance;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @var integer
*
* @ORM\Column(name="routenumber", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $routenumber;
}
?>
Ik vind het vooral een vage foutmelding aangezien deze uit Symfony zelf komt. Deze foutmelding krijg ik bij andere entiteiten ook (zoals Cursist die een Person is).
Weet iemand wat ik moet doen om dit op te lossen?
Roel
Gewijzigd op 11/09/2015 15:22:33 door Roel -
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
/**
* @ORM\ManyToOne(targetEntity="Route")
* @ORM\JoinColumn(name="route_id", referencedColumnName="routenumber")
**/
private $route;
?>
/**
* @ORM\ManyToOne(targetEntity="Route")
* @ORM\JoinColumn(name="route_id", referencedColumnName="routenumber")
**/
private $route;
?>
p.s. Waarom heet routenumber niet gewoon id?
Gewijzigd op 11/09/2015 17:37:14 door Frank Nietbelangrijk
Wat betreft routenumber in plaats van ID: ik doe dit project samen met een klasgenoot en die is zelf na jaren overtuigd door een docent dat het soms beter is om ...number te gebruiken in plaats van ID. Ik ben het er ook niet echt mee eens, maar soms krijg je iemand niet overtuigd om een bepaalde naamgeving te volgen, haha. :-)
Gewijzigd op 13/09/2015 22:34:49 door Roel -
Ook dat heeft helaas geen effect.
Voordat je dat doet wel even een backup maken van je database.
Toevoeging op 14/09/2015 19:52:15:
Tevens even debuggen met een simpele echo om te zien welke code nog wel wordt uitgevoerd en welke niet.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ride
*
* @ORM\Table(name="rides")
* @ORM\Entity
*/
class Ride
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=50, nullable=true)
*/
private $description;
/**
* @var boolean
*
* @ORM\Column(name="retour", type="boolean", nullable=true)
*/
private $retour;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @ORM\ManyToOne(targetEntity="Route")
* @ORM\JoinColumn(name="route_id", referencedColumnName="id")
**/
private $route;
}
?>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ride
*
* @ORM\Table(name="rides")
* @ORM\Entity
*/
class Ride
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=50, nullable=true)
*/
private $description;
/**
* @var boolean
*
* @ORM\Column(name="retour", type="boolean", nullable=true)
*/
private $retour;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
/**
* @ORM\ManyToOne(targetEntity="Route")
* @ORM\JoinColumn(name="route_id", referencedColumnName="id")
**/
private $route;
}
?>
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Route
*
* @ORM\Table(name="routes")
* @ORM\Entity
*/
class Route
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="from", type="string", length=50, nullable=false)
*/
private $from;
/**
* @var string
*
* @ORM\Column(name="addressfrom", type="string", length=50, nullable=false)
*/
private $addressfrom;
/**
* @var string
*
* @ORM\Column(name="postalcodefrom", type="string", length=7, nullable=false)
*/
private $postalcodefrom;
/**
* @var string
*
* @ORM\Column(name="to", type="string", length=50, nullable=false)
*/
private $to;
/**
* @var string
*
* @ORM\Column(name="addressto", type="string", length=50, nullable=false)
*/
private $addressto;
/**
* @var string
*
* @ORM\Column(name="postalcodeto", type="string", length=7, nullable=false)
*/
private $postalcodeto;
/**
* @var string
*
* @ORM\Column(name="distance", type="decimal", precision=5, scale=2, nullable=false)
*/
private $distance;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
}
?>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Route
*
* @ORM\Table(name="routes")
* @ORM\Entity
*/
class Route
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="from", type="string", length=50, nullable=false)
*/
private $from;
/**
* @var string
*
* @ORM\Column(name="addressfrom", type="string", length=50, nullable=false)
*/
private $addressfrom;
/**
* @var string
*
* @ORM\Column(name="postalcodefrom", type="string", length=7, nullable=false)
*/
private $postalcodefrom;
/**
* @var string
*
* @ORM\Column(name="to", type="string", length=50, nullable=false)
*/
private $to;
/**
* @var string
*
* @ORM\Column(name="addressto", type="string", length=50, nullable=false)
*/
private $addressto;
/**
* @var string
*
* @ORM\Column(name="postalcodeto", type="string", length=7, nullable=false)
*/
private $postalcodeto;
/**
* @var string
*
* @ORM\Column(name="distance", type="decimal", precision=5, scale=2, nullable=false)
*/
private $distance;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
*/
private $active;
}
?>
Code (php)
1
2
3
4
5
2
3
4
5
app/console doctrine:generate:entities AppBundle:Ride
app/console doctrine:generate:entities AppBundle:Route
app/console doctrine:update:schema --force
app/console doctrine:generate:crud AppBundle:Ride
app/console doctrine:generate:crud AppBundle:Route
app/console doctrine:generate:entities AppBundle:Route
app/console doctrine:update:schema --force
app/console doctrine:generate:crud AppBundle:Ride
app/console doctrine:generate:crud AppBundle:Route
Dat lukte allemaal nog maar toen wilde ik om te beginnen de aangemaakte RouteController testen door naar ../app_dev.php/route te surfen en toen kreeg ik de eerste foutmelding:
De Entity Route bestond al en kon niet nog een keer aangemaakt worden. (even vrij vertaald)
Deze naam 'Route' voor de entity is dus erg ongelukkig gekozen en die moet je dus een andere naam geven.
Daarna Kon ik wel de lijst met 'Routes' opvragen, die overigens nog leeg was dus probeerde ik er een aan te maken. Het formulier verscheen gewoon ik ik alle velden even iets ingevuld en op 'Create' gedrukt.
Toen kreeg ik weer een foutmelding dat de query mislukt was. Even nagezocht en blijkt dat 'from' en 'to' gereserveerde woorden zijn in MySQL. Die kolomnamen moet je dus veranderen in de annotations.
Daarna werkte het wel en ging ik kijken of RideController werkte. Ook hier ging het heel even fout.
MyRoute zoals ik de entity inmiddels genoemd had, had geen __toString() method. Dus die heb ik even aangemaakt:
Code (php)
Daarna ging het wel goed. En dat verbaasde mij enigzins omdat 'date' een keyword is. Ik wil je dus adviseren om ook deze kolomnaam te wijzigen.
Ik kreeg overigens niet jouw foutmelding.
Gewijzigd op 16/09/2015 20:53:14 door Frank Nietbelangrijk
Als ik met Xdebug door het proces loop, dan zie ik dat de entity nog goed opgehaald wordt, maar wanneer ik hem serialize, dan krijg ik de exception.
Code (php)
1
2
3
4
2
3
4
<?php
$entity = $repository->find($id);
$test = $serializer->serialize($entity, 'json');
?>
$entity = $repository->find($id);
$test = $serializer->serialize($entity, 'json');
?>
Regel 1 (38 in de controller, bovenaan deze pagina) gaat nog goed. De regel erna geeft de fout.
Googelen op deze foutmelding levert ook weinig zinnigs op; fetch="EAGER" heb ik ook geprobeerd, tevergeefs.
Toevoeging op 16/09/2015 21:29:09:
Het heeft sowieso te maken met de referentie naar een andere klasse, want Ride ophalen en serializen werkt namelijk wel. Het lijkt wel een fout in Symfony of Doctrine.
Ik betwijfel of dat het probleem is. Ik gok eerder dat je beter 'private' naar 'protected' kunt omzetten, zou je dat willen proberen?
Edit: Vergeet niet erna je cache op te schonen.
Gewijzigd op 16/09/2015 22:31:02 door DavY -
Verder:
wat geeft
Runtime Notice: Accessing static property Proxies\__CG__\AppBundle\Entity\Route::$lazyPropertiesDefaults as non static
Heb je het ook geprobeerd met fetch="EAGER", nadat je properties omgezet had naar protected?
Hartstikke bedankt allebei voor nu!
Graag gedaan :) De reden waarom het niet werkte is omdat jij jouw properties op 'private' had staan.
Ik snap alleen nog steeds niet waarom het niet direct werkte toen ik fetch="EAGER" toevoegde. Dat ga ik straks nog eens nader onderzoeken.
Misschien omdat je eerst jouw cache niet opgeschoond had? Normaal hoort hij gewoon op 'protected' te staan, als je wilt serializen in Symfony, maar ik kan me natuurijk vergissen, want ik ben zelf eigenlijk nog maar een 'leerling' in Symfony :P. Het lukt nu in ieder geval en bleek fetch="EAGER" toch wel de juiste.
http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html
Toen kreeg ik dus een map Resources met mapping-informatie. Toen ik stap voor stap alles ging nalopen, kwam ik erachter dat nadat ik deze map verwijderde, het probleem ook niet meer voorkwam. Het blijkt dus dat ik in het bijbehorende XML-bestand ook fetch="EAGER" moest toevoegen aan de relatie met Route.
Kan iemand mij vertellen waarom ik dan nu nog steeds annotations bij m'n klassen heb?
Toevoeging op 17/09/2015 17:22:39:
Gelijk even geprobeerd de annotations weg te halen. Het lijkt nog steeds te werken. Wat is het nut van deze annotations nu nog?
Blijkbaar is er voor gekozen om XML en annotations aan te maken? Annotations worden het meest gebruikt icm met Doctrine maar je mag uiteindelijk zelf kiezen wat je prettig vindt. Ik vind zelf het fijne aan annotations dat je geen apart config file hoeft te openen maar dat ze gewoon bij je properties staan en zo ook met de routes bij je Action methods in de controllers.
In de tutorial wordt echter standaard XML gebruikt en krijg je een tip als je YAML wil gebruiken. Hoe zit dit dan voor annotations? Weet jij of en hoe dat eventueel kan?