Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Symfony]SF2 return json...
Forum PHP.pl > Forum > PHP > Frameworki
Szymciosek
Witam,
chciałem zobaczyć jak Symfony2 poradzi sobie jako API i w tym celu chciałem wykorzystać ActionScript3.0 + Symfony2 (PHP) więc mam sobie jakąś aplikację, która wysyła z flasha json ( { id:1 } )
więc flash oczekuje wszystkich kolumn, które należą do ID = 1. Jako z tym nie mam problemu, bo normalnie mogę zrobić to tak:

  1. $json = json_decode($_POST['data']); //odebranie json z flash
  2.  
  3. $query = mysql_query("SELECT * FROM user WHERE id=$json->id"); //zapytanie do bazy
  4.  
  5. while($data = mysql_fetch_object($query))
  6. {
  7. $object = array(
  8. 'user' => $data,
  9. );
  10. } //uzupełnienie tablicy $object danymi z bazy
  11. print json_encode($object); //zwrócenie json do flasha


ale jak to samo zrobić w Symfony2 ?

  1. <?php
  2.  
  3. namespace SimonMedia\ApiBundle\Controller;
  4.  
  5. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  6. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  7. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  8.  
  9. class DefaultController extends Controller
  10. {
  11. /**
  12.   * @Route("/")
  13.   * @Template()
  14.   */
  15. public function indexAction()
  16. {
  17. return array();
  18. }
  19.  
  20. /**
  21.   * @Route("/user/get/all")
  22.   */
  23. public function getAll()
  24. {
  25. $em = $this->getDoctrine()->getEntityManager();
  26. $entities = $em->getRepository('SimonMediaApiBundle:Userzy')->findAll();
  27.  
  28. var_dump($entities);
  29. }
  30. }


var_dump zwraca
  1. array (size=3)
  2. 0 =>
  3. object(SimonMedia\ApiBundle\Entity\Userzy)[211]
  4. private 'id' => int 1
  5. private 'name' => string 'Janusz' (length=6)
  6. private 'email' => string 'Janko@wp.pl' (length=11)
  7. private 'phone' => string '668559634' (length=9)
  8. 1 =>
  9. object(SimonMedia\ApiBundle\Entity\Userzy)[206]
  10. private 'id' => int 2
  11. private 'name' => string 'Marek' (length=5)
  12. private 'email' => string 'Maro@wp.pl' (length=10)
  13. private 'phone' => string '665583999' (length=9)
  14. 2 =>
  15. object(SimonMedia\ApiBundle\Entity\Userzy)[207]
  16. private 'id' => int 3
  17. private 'name' => string 'Mariusz' (length=7)
  18. private 'email' => string 'Mario@wp.pl' (length=11)
  19. private 'phone' => string '994888777' (length=9)


Ale nijak nie potrafię się po tym ruszyć...

EDIT::
Doszedłem do tego, że po zmianie w klasie Userzy.php (entity) z private na public mam dostęp do np

print $entities[0]->name;

w przeciwnym razie otrzymuję error
Cannot access private property SimonMedia\ApiBundle\Entity\Userzy::$name in F:\WORK\labs\Symfony\ApiID\src\SimonMedia\ApiBundle\Controller\DefaultController.php on line 28

Ale chyba taka zmiana nie jest dobra i nie na tym to ma polegać ?
marcio
Pokaz klase entity, powinienes w niej miec gettery/settery zeby uzywac wlasciwosci klas private
Szymciosek
Owszem mam.

  1. Owszem mam.
  2.  
  3. <?php
  4.  
  5. namespace SimonMedia\ApiBundle\Entity;
  6.  
  7. use Doctrine\ORM\Mapping as ORM;
  8.  
  9. /**
  10.  * SimonMedia\ApiBundle\Entity\Userzy
  11.  *
  12.  * @ORM\Table()
  13.  * @ORM\Entity(repositoryClass="SimonMedia\ApiBundle\Entity\UserzyRepository")
  14.  */
  15. class Userzy
  16. {
  17. /**
  18.   * @var integer $id
  19.   *
  20.   * @ORM\Column(name="id", type="integer")
  21.   * @ORM\Id
  22.   * @ORM\GeneratedValue(strategy="AUTO")
  23.   */
  24. private $id;
  25.  
  26. /**
  27.   * @var string $name
  28.   *
  29.   * @ORM\Column(name="name", type="string", length=255)
  30.   */
  31. private $name;
  32.  
  33. /**
  34.   * @var string $email
  35.   *
  36.   * @ORM\Column(name="email", type="string", length=255)
  37.   */
  38. private $email;
  39.  
  40. /**
  41.   * @var string $phone
  42.   *
  43.   * @ORM\Column(name="phone", type="string", length=255)
  44.   */
  45. private $phone;
  46.  
  47.  
  48. /**
  49.   * Get id
  50.   *
  51.   * @return integer
  52.   */
  53. public function getId()
  54. {
  55. return $this->id;
  56. }
  57.  
  58. /**
  59.   * Set name
  60.   *
  61.   * @param string $name
  62.   */
  63. public function setName($name)
  64. {
  65. $this->name = $name;
  66. }
  67.  
  68. /**
  69.   * Get name
  70.   *
  71.   * @return string
  72.   */
  73. public function getName()
  74. {
  75. return $this->name;
  76. }
  77.  
  78. /**
  79.   * Set email
  80.   *
  81.   * @param string $email
  82.   */
  83. public function setEmail($email)
  84. {
  85. $this->email = $email;
  86. }
  87.  
  88. /**
  89.   * Get email
  90.   *
  91.   * @return string
  92.   */
  93. public function getEmail()
  94. {
  95. return $this->email;
  96. }
  97.  
  98. /**
  99.   * Set phone
  100.   *
  101.   * @param string $phone
  102.   */
  103. public function setPhone($phone)
  104. {
  105. $this->phone = $phone;
  106. }
  107.  
  108. /**
  109.   * Get phone
  110.   *
  111.   * @return string
  112.   */
  113. public function getPhone()
  114. {
  115. return $this->phone;
  116. }
  117. }
marcio
Nie rozumiem.

Robisz petle na obiekcie i potem wywolujesz gettery
Szymciosek
Czyli jak ?
Mam problem nawet z dostaniem się do pojedynczej wartości z array/object a o pętli nie wspomnę...

Chociaż chwila, bo chyba coś mam...
m44
Tworzysz sobie w repozytorium "Userzy" metodę, która zwraca kolekcję użytkowników jako tablicę.
W kontrolerze przekazujesz zmienną do widoku. Jeśli chcesz używać adnotacji @Template nie zapomnij, żeby w szablonie przekonwertować tą zmienną do json.
Możesz też od razu w repozytorium potraktować zwróconą kolekcję json_encode, jak zrobisz zależy od Twojej organizacji kodu.

Inna ważna sprawa, to odpowiedni nagłówek w odpowiedzi zwrotnej. Możesz w routingu zdefiniować domyślny parametr, który nazywa się "_format" i wtedy Symfony automatycznie wyśle odpowiedni nagłówek.
Możesz też ustawić nagłówek bezpośrednio w kontrolerze manipulując obiektem Response. Nie zapominaj, że kontroler w Symfony powinien zwrócić obiekt Response, a to czy go zwracasz pomagając sobie adnotacjami, czy przez metody pomocnicze kontrolera to sprawa drugorzędna.

Nie rozumiem, dlaczego próbujesz dostać się do właściwości obiektu nie używając metod dostępowych. Przecież operujesz na obiektach, wiec po to masz w klasie Entity metody dostępowe (get* i set*), żeby ich używać, a nie bezpośrednio używać prywatnych czy chronionych właściwości.
Szymciosek
Możesz powiedzieć coś więcej o tej metodzie ?

Potrzebuję uzyskać coś takiego:
Kod
{ user: [ {name:Janusz}, {name:Marek}, {name:Mariusz} ] }


a na razie mam:
Kod
{"user":{"name":"Mariusz"}}

za pomocą takiego kodu, co w ogóle nie jest zautomatyzowane za bardzo...
  1. foreach ($entities as $entity)
  2. {
  3. $data = array(
  4. 'user' => array(
  5. 'name' => $entity->getName()
  6. ),
  7. );
  8. }
  9. print json_encode($data);
phpion
Podstawy budowy tablic się kłaniają...
  1. $data['user'][] = array('name' => $entity->getName());
Szymciosek
Z takim problemem sobie poradziłem i działa ok dzięki wam, ale mam następny problem, w poprzednim miałem podane ID, a teraz potrzebuję skorzystać z findAll();

doszedłem do takiego miejsca i dalej nie wiem co...

  1. foreach ($entities as $entity)
  2. {
  3. //tak powinno wyglądać
  4. //$data[] = array('name' => $entity->getName(), 'phone' => $entity->getPhone());
  5. foreach ($cols as $col)
  6. {
  7. $method = 'get' . $col;
  8. $data[] = array($col => $entity->$method());
  9. print_r($data);
  10. }
  11. }


Niestety zwracana $data w postaci json wygląda tak:
Kod
{"data":{"users":[{"name":"Janusz"},{"phone":"668559634"},{"name":"Marek"},{"phone":"665583999"},{"name":"Mariusz"},{"phone":"994888777"}]}}


a powinien:
Kod
{"data":{"users":[{"name":"Janusz","phone":"668559634"},{"name":"Marek","phone":"665583999"},{"name":"Mariusz","phone":"994888777"}]}}


Czyli gdzieś za dużo razy tworzony jest array, zakładam, że w drugiej pętli foreach, ale tak jakoś musiałbym to mieć...

tablica $cols jest tworzona:
  1. foreach ($this->getDataFromPost()->cols as $col)
  2. {
  3. $cols[] = $col;
  4. }


Kombinowałem na różne sposoby z $data[$cols], $data = array..., $data[], explode, ale nie mogę wpaść na pomysł jak to rozwiązać...

Proszę o pomoc.
marcio
Tak na szybko:
  1. foreach ($cols as $key => $value)
  2. {
  3. $method = 'get' . $value;
  4. $data[] = array($key => $entity->$method());
  5. print_r($data);
  6. }

Albo jakos tak nie wiem dokladnie jak wyglada $cols.
phpion
Może warto najpierw zapoznać się z podstawami PHP, a nie rzucać się od razu na frameworka i to na dodatek takiego, który do najprostszych nie należy. Masz problem, gdyż za każdym razem dokładasz nowy element do tablicy "głównej". Wewnątrz drugiej pętli powinieneś stworzyć strukturę pojedynczego użytkownika, a poza tą pętlą dokładać ją do tablicy głównej. Wykorzystaj zatem jeszcze jedną tablicę pomocniczą. Nie zapomnij jednak o jej zerowaniu w odpowiednim miejscu. Kombinuj!
Szymciosek
Niestety, próbowałem już chyba na wszystkie sposoby, próbuję od 2 dobrych dni... i bezskutecznie...

teraz po Twojej wypowiedzi próbowałem coś na wzór:
  1. foreach ($this->getDataFromPost()->cols as $col)
  2. {
  3. $method = 'get' . $col;
  4. }
  5.  
  6. foreach ($entities as $entity)
  7. {
  8. foreach ($this->getDataFromPost()->cols as $col)
  9. {
  10. $method = 'get' . $col;
  11. $methods[] = $method;
  12. $cols[] = $col;
  13. }
  14.  
  15. for ($i = 0; $i < count($methods); $i++)
  16. {
  17. $tmp[] = array('name' => $entity->$methods[$i]());
  18. }
  19.  
  20. $data[] = $tmp;
  21. $tmp = array();
  22.  
  23. $cols = array();
  24. $methods = array();
  25. }


lecz też bezskutecznie, bo otrzymuję:
Kod
{"data":{"users":[[{"name":"Janusz"},{"name":"668559634"}],[{"name":"Marek"},{"name":"665583999"}],[{"name":"Mariusz"},{"name":"994888777"}]]}}
, a miałoby to być parami, do tego miałoby to być name, phone, name, phone, name, phone itd...

cols wygląda tak:
Kod
Array
(
    [0] => name
    [1] => phone
)


Zaprzęgnąłem do tego również znajomego, który jest o wiele lepszym programistą ode mnie, ale niestety też nie pomógł... Czy to w ogóle jest możliwe do zrobienia ?
Przepraszam za zawracanie wam tyłka, ale naprawdę już nie wiem co i jak zrobić żeby to osiągnąć... szukam, czytam o tablicach, próbuję swoimi sposobami, spróbówałem z $tmp i nic...
phpion
Spróbuj tego:
  1. $data = array(
  2. 'data' => array(
  3. 'users' => array()
  4. )
  5. );
  6.  
  7. foreach ($entities as $entity) {
  8. $tmp = array();
  9.  
  10. foreach ($this->getDataFromPost()->cols as $col) {
  11. $method = 'get'.$col;
  12.  
  13. $tmp[$col] = $entity->$method();
  14. }
  15.  
  16. $data['data']['users'][] = $tmp;
  17. }

Pisane oczywiście w ciemno, ale wydaje mi się, że powinno być ok.
Szymciosek
Działa bardzo dobrze, ale chyba nie wpadłbym na to tak szybko, chociaż z tego co widzę - co zrobiłem - to krążyłem bardzo blisko chyba ?
Dziękuję Ci bardzo dobry człowieku wink.gif


Czyli albo źle składałem $tmp, albo źle później składałem $data itd...
netrat
Osobiście sugerowałbym wpierw zbudowanie odpowiedniej tablicy, z encji poprzez użycie metody toArray() którą można sobie zdefiniować w modelu a następnie zwrócenie odpowiedniego response. Trochę może zamotałem więc pokaże w kodzie o co mi chodzi
  1. $responseArray = array();
  2. foreach ($listaElementół as $element){
  3. $responseArray[] = $element->toArray();
  4. }
  5. $response = new Response(json_encode( $responseArray ));
  6. $response->headers->set('Content-Type', 'application/json');
  7. return $response;

Tu już chodzi mi tylko o to aby rozwiązanie było nieco bardziej eleganckie smile.gif
Szymciosek
No a jak wtedy wygląda metoda toArray ?
ano
Zamiast ręcznie transformować obiekty na tablice, potem json_encode to radzę zainteresować się świetnym Bundlem:
http://jmsyst.com/bundles/JMSSerializerBundle
Adnotacjami w modelach oznaczasz jak ma być serializowany/deserializowany. A potem bezproblemowo serializujesz do xmla/jsona.
Szymciosek
Witam ponownie, nie chciałem zakładać nowego tematu tylko zapytam tutaj.

Jak sprawdzić czy dana kolumna istnieje nie pobierając z entity wszystkich danych ?

Tzn. mam takiego coś:
  1. $entityManager = $this->getDoctrine()->getEntityManager();
  2. $entities = $entityManager->getRepository('ApiRestServiceBundle:Users');


i teraz jeśli zrobię
  1. var_dump($entitites);


otrzymuję:
Kod
object(Doctrine\ORM\EntityRepository)[364]
      ...
      public 'fieldNames' =>
        array (size=13)
          'id' => string 'id' (length=2)
          'username' => string 'username' (length=8)
          'first_name' => string 'first_name' (length=10)
          'last_name' => string 'last_name' (length=9)
          'email_address' => string 'email_address' (length=13)
          'locale' => string 'locale' (length=6)
          'password' => string 'password' (length=8)
          'created' => string 'created' (length=7)
          'last_login' => string 'last_login' (length=10)
          'user_rank' => string 'user_rank' (length=9)
          'active' => string 'active' (length=6)
          'user_token' => string 'user_token' (length=10)
          'ip_address' => string 'ip_address' (length=10)
      public 'columnNames' =>
        array (size=13)
          'id' => string 'id' (length=2)
          'username' => string 'username' (length=8)
          'first_name' => string 'first_name' (length=10)
          'last_name' => string 'last_name' (length=9)
          'email_address' => string 'email_address' (length=13)
          'locale' => string 'locale' (length=6)
          'password' => string 'password' (length=8)
          'created' => string 'created' (length=7)
          'last_login' => string 'last_login' (length=10)
          'user_rank' => string 'user_rank' (length=9)
          'active' => string 'active' (length=6)
          'user_token' => string 'user_token' (length=10)
          'ip_address' => string 'ip_address' (length=10)
      ...


I oczywiście chce się dostać do fieldNames lub columnNames, bo widzę, że to, to samo więc nie powinno robić różnicy, które wybiorę.

Podczas pobrania wszystkich danych za pomocą findAll() zadanie byłoby ułatwione, bo dostanę wtedy wszystkie kolumny i ich wartości, lecz nie o to mi chodzi, bo przyjdzie sytuacja, w której będę musiał pobrać tylko username i first_name i co wtedy ?

Za pomocą createQuery("SHOW COLUMNS... nie jestem w stanie tego zrobić, bo wywala mi błąd, że takie rzeczy jak INSERT INTO czy właśnie SHOW, bo otrzymuję: [Syntax Error] line 0, col 0: Error: Expected SELECT, UPDATE or DELETE, got 'SHOW'
m44
To wszystko jest opisane w dokumentacji Doctrine.

Z treści twojego pytania nie wiadomo, czy chcesz sprawdzić, czy kolumna fizycznie istnieje w bazie, czy tylko chcesz wybrać wybrane kolumny podczas budowania zapytania.
Zrozumiałem, że chodzi o to drugie. W takim wypadku tworzysz sobie w twoim repozytorium metodę zwracającą to co ma zwracać, np. za pomocą QueryBuilder.
Szymciosek
Właśnie chodzi o to czy kolumna istnieje w bazie, bo żeby wszystko zadziałało Entity musi być zbudowane na podstawie bazy, więc musi z nią współpracować.

Normalnie zrobiłbym np. coś w tym stylu:
Kod
SHOW COLUMNS FROM users WHERE Field='id';


następnie sprawdzał czy zapytanie coś zwraca i jeśli zwraca, to istnieje, jeśli nie to error.

Równie dobrze mogę sprawdzać czy istnieje metoda w klasie Entity, ale to rozwiązanie nie jest dobre, bo musiałbym przelecieć przez array w pętli i sprawdzać, które istnieją itd...

m44 możesz powiedzieć coś więcej ?
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.