jQuery ajax вызов не может прочитать json закодированные данные из Cake PHP 3.8 (получает пустой массив) - PullRequest
2 голосов
/ 06 февраля 2020

У меня странная проблема с чтением кодированных json данных, возвращаемых моим CakePHP3 API в ответ на вызов ajax из jQuery. Я уже прочитал более 20 сообщений о stackoverflow и в других местах и ​​о обычных проблемах, с которыми сталкивались люди, когда из-за неправильного dataType, contentType или сервер не получал данные из ajax. Ни один из этих случаев здесь не применим (я пробовал разные настройки, не влияющие на мою проблему).

Проблема:

Мой ajax вызов отправляет некоторые параметры В моем API CakePHP3 API правильно получает параметры и возвращает закодированный json массив сущностей Cake PHP (каждая сущность имеет дополнительное свойство 'available_yield', добавленное перед отправкой обратно в вызов ajax). Я получаю правильный вывод, используя прямой URL в браузере (проверил его с помощью json валидаторов, все хорошо), но мой вызов ajax (я использовал вкладки консоли и сети в Chrome devtools для исследования) показывает пустой массив для правильно сформированного json.

Мое исследование показало, что проблема возникает, когда я изменяю сущности Cake PHP. Если я верну исходные данные из закодированного API json, jquery ajax получит правильные данные. Но когда я изменяю любую сущность, массив в jquery ajax пуст.

Отладка из Cake PHP показывает, что оба массива (неизмененные и измененные) выглядят одинаково, за исключением добавленного свойства, т.е. они корректны и во всех отношениях ОК, оба в json, оба в браузере в порядке. Но измененный вариант не принимается jquery как json.

На данный момент решение выглядит следующим образом: не изменяйте свои данные! Но это то, что мы делаем на сервере перед отправкой соответствующих и обработанных данных клиенту, не так ли?

У кого-нибудь была подобная проблема?

Я прилагаю свой код:

Торт PHP Функция API:

function myFunction(){
$params = $this->getRequest()->getQueryParams();
        //debug($params);
        $componentReference = $params['component_reference'];
        $componentTypeId = $params['component_type_id'];

        $matchingCrops = $this->Crops->find()->select(['id', 'grower_name', 'bulk'])->where(['reference' => $componentReference]);

        $cropsWithYieldInfo = []; //to hold modify crop
        foreach($matchingCrops as $crop){
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a string
            if(isset($availableYield) && !empty($availableYield)){
                $crop->available_yield = number_format($availableYield,1);  //tried $crop['available_yield'] as well, same result
                $cropsWithYieldInfo[] = $crop;
            }
        }

//        debug($cropsWithYieldInfo);
//        debug($matchingCrops);

        //$content = json_encode($cropsWithYieldInfo);  // <<-- changing to $matchingCrops makes ajax see the array, but the array does not have my calculated data
        $content = json_encode($matchingCrops);

        $this->response = $this->response->withStringBody($content);
        $this->response = $this->response->withType('json');  
        $this->autoRender = false; 
        return $this->response;
} 

my AJAX:

function myAjax(){
 $.ajax({
                type: 'GET',
                url: url,
                //contentType: "application/json",
                dataType: "json"
            })
            .done(function (data) {
                console.log(data);  
            })
            .fail(function (data) {
                console.log('AJAX call to /'+errMsg+' function failed');
            })
}

JSON данные, возвращаемые из API:

РЕДАКТИРОВАТЬ: Может быть важно: Когда я обращаюсь к API через URL в браузере, он всегда возвращает измененные данные; похоже, мой код изменяет фактические сущности в наборе $ matchCrops. Таким образом, если установить для $ content значение $ MatchCrops или $ cropWithYieldInfo, результат в браузере всегда будет одинаковым. Но он отличается при доступе к API через ajax: когда $ content = json_encoded ($ MatchCrops) я получаю исходный неизмененный массив данных, когда $ content = json_encoded ($ cropWithYieldInfo) я получаю пустой массив.

Это действительно странно: почему браузер всегда получает модифицированный массив, а ajax получает либо один, либо другой ??? Я понимаю, что если я изменяю сущность $ crop, то она изменяет сущность в результирующем наборе, но я ожидаю, что это будет согласованно как для браузера, так и для вызова ajax.

РЕДАКТИРОВАТЬ: я попытался слегка изменить код, позволяющий увидеть, будут ли какие-либо изменения в клонировании сущностей, но единственное отличие состоит в том, что теперь браузер получает то, что я ожидаю (либо исходный неизмененный массив, либо модифицированный), и это согласуется с тем, что получает ajax. Но это не решает проблему (ajax по-прежнему получает пустой массив, если массив был изменен).

foreach($matchingCrops as $crop){
            $modCrop = clone $crop;
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a string
            if(isset($availableYield) && !empty($availableYield)){
                $modCrop->available_yield = number_format($availableYield,1);  //tried $crop['available_yield'] as well, same result
                $cropsWithYieldInfo[] = $modCrop;
            }
        }

Modified (ajax получает это как пустой массив; браузер всегда получает это из API):

[{"id":12345,"grower_name":"XYZ","bulk":false,"available_yield":"4.1"},{"id":23456,"grower_name":null,"bulk":true,"available_yield":"190.0"}]

Не изменено (ajax получает это правильно):

[{"id":12345,"grower_name":"XYZ","bulk":false},{"id":23456,"grower_name":null,"bulk":true}]

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020
$array = ['foo'=>'bar'];

$this->set([
    'response' => $array,
    '_serialize' => 'response',
]);
$this->Request->renderAs($this, 'json');

И чем бы я сериализировал ajax! Таким образом, вам не нужно было бы структурировать объект, вы могли бы использовать его непосредственно для свойства данных.

$.ajax({
    type: 'POST',
    url: url,                      
    data: {YourArray: YourVariables},
    success: function(data) {
      alert(data);
    }
});

Вы можете найти больше здесь: https://api.jquery.com/serialize/

Давайте посмотрим на ajax, вам нужно передать значения из массива в ajax, чтобы получить ответ от каждого значения, которое вы пытаетесь сделать +errMsg+.

Ваш ajax должен выглядеть так в случае неудачи и успеха:

сбой: function( jqXHR, Status, errMsg) { тогда вы можете отобразить ответ как console.log('AJAX call to /'+errMsg+' function failed');

$.ajax({
    type: "GET",
    url: url,
    data: {
      title: $(value[0]).val(),
      description: $(value[1]).val()
    },
    success: function (data) {
        if(data === "success") {
            // do something with data or whatever other data on success
            console.log('success');
        } else if(data === "error") {
            // do something with data or whatever other data on error
            console.log('error');
        }
    }
});

Показать Указанную ошибку необходимо передать title = $(value[0]).val() в функции успеха.

Или использовать ajax serializeArray() и each() пример здесь https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_ajax_serializearray

С выпечкой PHP 3.4, использование должно использовать

$content = json_encode($matchingCrops);
return $this->response->withType("application/json")->withStringBody($content);

вместо этого

$content = json_encode($matchingCrops);

$this->response = $this->response->withStringBody($content);
$this->response = $this->response->withType('json');  
$this->autoRender = false; 
return $this->response;
0 голосов
/ 07 февраля 2020

OMG ... нашел это! Ладно, это неловко, но я все еще собираюсь опубликовать это как УРОК и предупреждение другим людям: , если у вас есть проблема вечером, которую вы не можете решить, go домой и у вас есть хороший спать, начать снова утром!

Причины проблемы:

1) моя функция вычисления фактически возвращала число, а не строку, и я проверял пустоту, поэтому когда он возвращал 0, код не добавлял свойство 'available_yield' к сущности $ crop (потому что строка кода, ответственная за это, также была в неправильном месте! должна была быть вне блока if)

В этот момент я все еще говорил «хорошо, но я должен получить согласованное поведение как в браузере, так и в ajax call !!!», если только ...

2) Я не заметил, что Я использовал другой идентификатор для проверки браузера и для вызова ajax, поэтому компьютер был прав ...: - /

Всегда учусь ...

  • double- проверить каждую строку и отладить все позиции sible vars!
  • перепроверьте ваши ТЕСТОВЫЕ ДАННЫЕ!

Версия кода, которая работает нормально:

function myFunction(){
$params = $this->getRequest()->getQueryParams();
        //debug($params);
        $componentReference = $params['component_reference'];
        $componentTypeId = $params['component_type_id'];

        $matchingCrops = $this->Crops->find()->select(['id', 'grower_name', 'bulk'])->where(['reference' => $componentReference]);

        $cropsWithYieldInfo = []; //to hold modify crop
        $cropsWithYieldString = '';
        foreach($matchingCrops as $crop){
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a float not string! 
            if(isset($availableYield)){ //<<- that was the cause of the problem; !empty(float) will ignore 0, just check if it's set
                $crop->available_yield = number_format($availableYield,1); 
            }
            $cropsWithYieldInfo[] = $crop;
        }

//        debug($cropsWithYieldInfo);
//        debug($matchingCrops);

        $content = json_encode($cropsWithYieldInfo); 

        //$this->response = $this->response->withStringBody($content);
        //$this->response = $this->response->withType('application/json');  
        $this->autoRender = false; 
        //return $this->response;
        //more concisely
        return $this->response->withType('application/json')->withStringBody($content);

}

Спасибо за ваше время, ребята, вы заставил меня сосредоточиться на поиске решения.

...