web-dev-qa-db-ja.com

PHP symfony 4 ajaxフォームの送信

Ajaxを使用してフォームを送信し、そのデータをデータベースに送信しようとしていますが、ajax呼び出しから受信したデータを処理する方法がわかりません。

次のコードを書きました。

{% extends 'base.html.twig' %}

{% block title %}Assignments | CRM Fabriek{% endblock %}

{% block body %}

    <div class="container">
        <div class="columns">
            <div class="column is-full">
                <div class="level">
                    <h1 class="level-left title title-no-margin is-vcentered">Assignments</h1>

                    <div class="level-right">
                        {% include 'search.html.twig' %}

                        <a href="{{ path("newAssignment") }}" class="level-item button is-success">Add new</a>
                    </div>
                </div>
                <table class="table is-fullwidth">
                    <tr>
                        <th>Name</th>
                        <th>Description</th>
                        <th>Status</th>
                        <th>Actions</th>
                    </tr>
                    {% if assignments != null %}
                        {% for assignment in assignments %}
                            <tr>
                                <td>{{ assignment.name }}</td>
                                <td>{{ assignment.description }}</td>
                                <td>{{ assignment.status }}</td>
                                <td>
                                    <a class="button is-info is-small" href="{{ path('overviewAssignment', {'id': assignment.id}) }}"><i class="fa fa-eye"></i></a>
                                    <a class="button is-warning is-small" href="{{ path('editAssignment', {'id': assignment.id}) }}"><i class="fa fa-pencil"></i></a>
                                    <button class="button is-success is-small" onclick="openModal({{ assignment.id }})"><i class="fa fa-plus"></i></button>
                                </td>
                            </tr>
                        {% endfor %}
                    {% else %}
                        <tr>
                            <td colspan="4">No entries</td>
                        </tr>
                    {% endif %}
                </table>
                <div class="pagerfanta">
                    {{ pagerfanta(pager)}}
                </div>
                <div>
                    <div class="modal">
                        <div class="modal-background"></div>
                        <div class="modal-card">
                            <header class="modal-card-head">
                                <p class="modal-card-title">Modal title</p>
                            </header>
                            <section class="modal-card-body">
                                {{ form_start(form, {'attr': {'id': 'task_form'}}) }}
                                {{ form_row(form.name, {'attr': {'class': 'input'}}) }}
                                {{ form_row(form.description, {'attr': {'class': 'textarea'}}) }}

                                {{ form_label(form.status) }}
                                <div class="control">
                                    <div class="select">
                                        {{ form_widget(form.status) }}
                                    </div>
                                </div>

                                {{ form_end(form) }}
                            </section>
                            <footer class="modal-card-foot">
                                <button id="submit_task" class="button is-success">Save changes</button>
                                <button class="button" onclick="closeModal()">Cancel</button>
                            </footer>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

{% endblock %}

{% block javascripts %}
    <script>
        function openModal(id){
            $('.modal').addClass('is-active');
            $('#submit_task').attr('onClick', 'submitTask('+ id +');');
        }

        function closeModal(){
            $('.modal').removeClass('is-active');

        }
        function submitTask(id){
            console.log(id);

            form = $('#task_form').serialize();

            console.log(form); 
            $.ajax({
                url:'{{ path('submit_task') }}',
                type: "POST",
                dataType: "json",
                data: {
                    "task": form
                },
                async: true,
                success: function (return_data)
                {
                    console.log(return_data);
                },
                error: function (xhr, ajaxOptions, thrownError)
                {

                }
            });
            closeModal();
        }
    </script>
{% endblock %}

コントローラで次のメソッドを使用します:

/**
     * @Route("/", name="submit_task")
     */
    public function add_task(Request $request){
        $form_data = $request->get('task');

        return new JsonResponse($form_data);
    }

フォームはindexアクションで作成されます:

/**
     * @Security("is_authenticated()")
     * @Route("/assignments", name="assignments")
     */
    public function index(Request $request)
    {

        $assignment_rep = $this->getDoctrine()->getRepository(Assignment::class);

        if($request->get('search') == null) {
            if($this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')){
                $assignments = $assignment_rep->findAll();
            }
            else
            {
                /* @var User $user */
                $user = $this->getUser();
                $assignments = array();
                foreach($user->getAssignments() as $assignment){
                    array_Push($assignments, $assignment);
                }
            }
        }
        else{

            if($this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')){
                $assignments = $assignment_rep->search($request->get('search'));
            }
            else
            {
                /* @var User $user */
                $assignments = $assignment_rep->searchUser($request->get('search'), $this->getUser()->getId());
            }
        }

        $page = $request->query->get('page', 1);

        $adapter = new ArrayAdapter($assignments);

        $pagerfanta = new Pagerfanta($adapter);
        $pagerfanta->setMaxPerPage(25);
        $pagerfanta->setCurrentPage($page);


        $task = new Task();
        $form = $this->createForm(TaskFormType::class, $task);
        $form->remove('assignment');

        $assignments = $pagerfanta->getCurrentPageResults();

        return $this->render('assignment/index.html.twig', array(
            'assignments' => $assignments,
            'pager' => $pagerfanta,
            'form' => $form->createView()
        ));
    }

関数内の「フォーム」オブジェクトなしでフォームデータを処理する方法を教えてください。誰かがこの問題を助けてくれますか?

4
Sander Bakker

あなたの問題はSymfony3で偶然見つけたもので、それ以来、AJAX経由で送信されたコンテンツを処理するためにさまざまな方法を試してきました。

AJAXデータを取得する

この部分は単純です。コントローラでは、単に$data = $request->getContent();を呼び出します。

サービスを作成する

この種のデータの処理に役立つサービスを作成します。 Symfonyのバリデーター(コンストラクター:ValidatorInterfaceを参照)を使用し、_validateAndCreateと呼ばれるメソッドをパラメーター_$data_(AJAXリクエスト)として受け取りますbody content)と_$entityClassName_(データを作成してデータを入力するエンティティです。例:_User::class_はクラス名文字列を生成します)

ノーマライザ、エンコーダ、シリアライザ

これら3つは、AJAXコンテンツを処理する上で重要です。 _$data_は、逆シリアル化してエンティティインスタンスに直接注入できます(ClassNameパラメータから作成)。

データがJSONとして送信される場合は、JsonEncoderを使用します。シリアライザオブジェクトが作成されると、JSONデータを新しくインスタンス化されたオブジェクトに直接デシリアライズできます(パラメータのclassNameはオブジェクトの生成に使用されます)。

オブジェクトが生成されたら、バリデーターを使用して、それが有効なオブジェクトであるかどうかを確認します。バリデーターが適切に機能するように、すべてのEntityオブジェクトで_@Assert_を使用することをお勧めします。

_class ApiService
{

    private $validator;
    public function __construct(ValidatorInterface $validator)
    {
        $this->validator = $validator;
    }

    public function validateAndCreate($data, $entityClassName){

        $objectNormalizer = new ObjectNormalizer();
        $normalizers = [$objectNormalizer];
        $encoders = [new JsonEncoder()];
        $serializer = new Serializer($normalizers, $encoders);

        $result = $serializer->deserialize($data, $entityClassName, 'json');
        $errors = $this->validator->validate($result);

        if(count($errors) > 0){
            throw new CustomApiException(Response::HTTP_BAD_REQUEST, (string) $errors);
        }

        return $result;

    }
}
_

新しい会議を作成するためのコントローラーでの使用例

_public function newMeeting(Request $request, ApiService $apiService, FractalService $fractalService){
        /** @var Admin $admin */
        $admin = $this->getUser();

        /** @var UserRepository $rep */
        $em = $this->getDoctrine()->getManager();

        $data = $request->getContent();

        if(empty($data)) throw new CustomApiException(Response::HTTP_BAD_REQUEST, "Data sent null.");

        /** @var Meeting $test */
        $meeting = $apiService->validateAndCreate($data, Meeting::class);

        $meeting->setAdmin($admin);
        $admin->addMeeting($meeting);

        $em->persist($test);
        $em->flush();

        //Return data however you wish, I use a FractalService

        $data = $fractalService->generateData($meeting, MeetingTransformer::class, 'meeting');
        return $fractalService->generateResponse($data);

    }
_

AJAXのフォーム処理のクライアント側の例

_$("#form").on("submit", function(e){
   e.preventDefault();
   let data = {};
   $(this).serializeArray().forEach((object)=>{
      data[object.name] = object.value;
   });
   console.log(data);
   
   //TODO: ajax call here with data
   //If ajax call fails because server can't decode
   //Think of doing : data = JSON.stringify(data);
   console.log(JSON.stringify(data));
   
})_
_<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="form">
  <input type="text" value="john" name="firstname"/>
  <input type="text" value="smith" name="lastname"/>
  <input type="text" value="florida" name="address"/>
  <input type="text" value="1234512345" name="phonenumber"/>
  <input type="text" value="[email protected]" name="email"/>

  <input type="submit" value="submit this"/>
</form>_
6
kemicofa ghost