为了账号安全,请及时绑定邮箱和手机立即绑定

Laravel 对象属性在循环时为空,但在 json_encode() 时具有值

Laravel 对象属性在循环时为空,但在 json_encode() 时具有值

PHP
HUX布斯 2021-11-13 16:19:26
我查看一段代码:  @forelse ($questions as $question)            <a href="#" class="list-group-item">                <h4>{{$question->title}}</h4>                <p>{{Str::limit($question->body, 100)}}</p>                <span>Asked {{$question->created}} by {{$question->user_name}}</span>                {{json_encode($question)}}            </a>        @empty            <h2>No questions!</h2>        @endforelse我得到这样的 html('...' 没问题,这是有效的,但值很长):<a href="#" class="list-group-item">            <h4>...</h4>            <p>...</p>            <span>Asked  by </span>             <br>            {"id":1,"title":"...","path":"...","body":"...","created":"2 hours ago","user_name":"Nash Bogisich","user_id":11}        </a>如您所见,在 htmlcreated中null,还有user_name. 但是在 json 中,这些 props 有它们的值。我不明白这是怎么可能的,请帮忙。QuestionResource 完全简单:class QuestionResource extends JsonResource{    public function toArray($request)    {        return [            'id' =>$this->id,            'title' =>$this->title,            'path' =>$this->path,            'body' =>$this->body,            'created' => $this->created_at->diffForHumans(),//            'modified' =>$this->modified_at,            'user_name' =>$this->user->name,            'user_id' =>$this->user_id,        ];    }}我也查了一下,created是真的null。if ($question->created==null){                        $created = 'null value';                    }是的,null价值。$questions 不是真正的数组(我认为),它是一个 Laravel 集合。控制器(我有两个,一个用于api,一个用于web.Web使用api。):应用程序接口:   public function index()    {        // now each transform of question use QuestionResource        return QuestionResource::collection(Question::latest()->paginate(15));    }网站:    public function index()    {        $questions = $this->apiController->index();//        return $questions;        return View::make('question.index')->with('questions', $questions);    }
查看完整描述

3 回答

?
素胚勾勒不出你

TA贡献1827条经验 获得超9个赞

首先,让我们稳定一下:


来自 PHP 的类型杂耍: ...变量的类型由使用该变量的上下文决定。


洞察力的简单示例:


$thisIsAnObject = new Object;

echo $thisIsAnObject;

// as echo expects a string, PHP will attempt to cast it to string and it

// will fail with a message similar to: "Object could not be converted to string"

然而,Laravel,特别是在 中Illuminate\Support\Collection,接管了这种自动转换并以其需要的类型提供上下文。


洞察力的简单示例:


$simpleCollection = collect([['key' => 'value'],['key' => 'value']]);

echo $simpleCollection; 

// instead of letting it error, laravel feeds the echo statement with the json

// representation of the collection (which is a string): [{"key":"value"},{"key":"value"}]

// check Illuminate\Support\Collection::__toString() function

Laravel 对以下内容完全相同foreach:


$collection = Question::latest()->paginate(15);

foreach ($collection as $model) {}

// collection feeds the foreach an array of models for better manusing.

// check Illuminate\Support\Collection::toArray() function

总而言之,请注意QuestionResource的映射发生在toArray()函数内部。


所以这:


$questionResource = QuestionResource::collection(Question::latest()->paginate(15));

如果您使用$questionResource上述内容提供 foreach ,则框架的意图是为 foreach 提供其集合模型的数组,而忽略您将所有内容转换为带有QuestionResource's 的原始数组的意图->toArray()。


但是,例如,如果您要传递$questionResource给json_encode()需要字符串且不涉及任何循环的函数,则框架可以首先解析QuestionResource's->toArray()然后将其转换为集合的 json 表示形式。


这就是你上面的代码发生的事情。


我将写两种解决这个问题的方法,从最快到最好:


1) 调用 ->toArray() 预先强制转换

$questions = $this->apiController->index()->toArray(request());

@forelse ($questions as $question)

    <a href="#" class="list-group-item">

        <h4>{{$question['title']}}</h4>

        <p>{{Str::limit($question['body'], 100)}}</p>

        <span>Asked {{$question['created']}} by {{$question['user_name']}}</span>

    </a>

@empty

    <h2>No questions!</h2>

@endforelse

2)为您的问题模型中的created和user_name字段创建一个访问器

class Question extends Model

{

    // $this->created

    public function getCreatedAttribute()

    {

        return $this->created_at->diffForHumans();

    }


    // $this->user_name

    public function getUserNameAttribute()

    {

        return $this->user->name;

    }

}

像这样,您通过在任何有问题模型实例的地方都可以使用这些字段来持久化这些字段。当然,这包括QuestionResource.


class QuestionResource extends JsonResource

{

    public function toArray($request)

    {

        return [

            'id' => $this->id,

            'title' => $this->title,

            'path' => $this->path,

            'body' => $this->body,

            'created' =>  $this->created,

            // 'modified' => $this->modified_at,

            'user_name' => $this->user_name,

            'user_id' => $this->user_id

        ];

    }

}

@forelse ($questions as $question)

    <a href="#" class="list-group-item">

        <h4>{{$question->title}}</h4>

        <p>{{Str::limit($question->body, 100)}}</p>

        <span>Asked {{$question->created}} by {{$question->user_name}}</span>

        {{json_encode($question)}}

    </a>

@empty

    <h2>No questions!</h2>

@endforelse

希望能帮助到你。


查看完整回答
反对 回复 2021-11-13
?
MYYA

TA贡献1868条经验 获得超4个赞

我认为您对 API 和 Web 响应有点困惑。


首先定义QuestionResourceAPI 资源,这很好,并且created正确放置了属性。


如果然后查看视图文件,您正在迭代一个集合,其中每个对象都是一个QuestionResource对象类型。


要获得您想要的属性,您应该打印出来,{{ $question->created_at->diffForHumans() }}因为您仍在与模型进行神奇的交互Question。


快速深入的解释

使用 API 资源时,您应该记住,PHP Object -> JSON string转换发生在控制器处理请求之后和路由器组件向用户发送响应之前的后期阶段,如果您从另一个方法调用您的方法,您仍然没有经历了 json 转换过程,因为它最终是核心代码后期阶段的一部分。


这意味着$question您从$this->apiController->index()调用中获得的返回值会为您提供 的集合QuestionResource,而不是响应的 json 字符串。


要了解Question即使您有一个QuestionResource对象数组,您如何仍然可以访问模型,您必须查看JsonResourcelaravel 核心中的类。


laravel 中的每个 API 资源类都继承自它。此类使用DelegateToResource定义此方法的特征(在 Laravel 的核心中也定义了该特征):


/**

 * Dynamically get properties from the underlying resource.

 *

 * @param  string  $key

 * @return mixed

 */

public function __get($key)

{

    return $this->resource->{$key};

}

这意味着当你有一个$question类型的对象时,QuestionResource你仍然可以通过简单地“假装”你的资源对象是一个问题来访问底层资源,因为它会像这样运行并且会自动将所有方法调用和读取的属性转发到问题模型。


如果您质疑您告诉 将模型本身QuestionResource包装在何处Question,您可以在 index 方法的 return 指令中做到这一点:


return QuestionResource::collection(Question::latest()->paginate(15));

您可以将此返回指令视为Question您作为参数传递给更大对象(QuestionResource类)中的每个模型的“简单”包装,该对象添加了一个方法以将问题表示为数组,因此将用于将模型转换为 json在稍后阶段(正如我之前提到的)。


因此,要在@foreach循环中正确检索字段,您必须引用模型属性的名称,而不是您定义为toArray()方法返回的名称,因为这将在稍后阶段执行。


那么,为什么要{{json_encode($question)}}输出资源中定义的字段呢?


因为JsonResourceclass 实现了 PHP 的JsonSerializable接口,这意味着它实现了jsonSerialize方法(由接口定义)来强制 json 转换。


当您调用json_encode方法时,php 将识别$question实现该接口并因此是可序列化的,因此它调用该jsonSerialize方法并获取所需的字符串。


结果,您获得了 api 资源输出并转换为 json,并且该输出具有您在QuestionResource类中定义的字段


查看完整回答
反对 回复 2021-11-13
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

这些解决方案对我有用:

  1. 只需拨打json_encodejson_decode在我的网站上QuestionController。很简单,不会产生太多的开销。但这是一种解决方法。

  2. Question Model类中指定属性获取访问器。微妙的。而Model类是定义存取正确的地方。现在我确实使用了该解决方案。

#1 的代码:

public function index()

{

    $str = json_encode($this->apiController->index());

    $questions = json_decode($str);


    return View::make('question.index')->with('questions', $questions);

}

#2 的代码:


// in Question model class...


// $this->created

public function getCreatedAttribute()

{

    return $this->created_at->diffForHumans();

}


// $this->user_name

public function getUserNameAttribute()

{

    return $this->user->name;

}


查看完整回答
反对 回复 2021-11-13
  • 3 回答
  • 0 关注
  • 139 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信