0

好的,情况就是这样。在我的beforeSave函数中,我想操作一些$this->request->data条目。

这是我的组件:

<?php
App::uses('Component', 'Controller');
class GetStationComponent extends Component {
    public function getStationId ($station) { 
        $stationInstance = ClassRegistry::init('Station');

        $conditions = array('OR' => array(
            array('Station.code LIKE' => $station),
            array('Station.naam LIKE' => $station),
            array('Station.naam_overig LIKE' => $station)
            ));

        $result = $stationInstance->find('list', array('conditions'=>$conditions));

        $value = array_values($result);
        $value = $value[0];

        return $value;
    }
}
?>

这是我的控制器中的 beforeSave 函数:

public function beforeSave($options = array()) {
        if (!empty($this->request->data['Experience']['vertrekstation']) && !empty($this->request->data['Experience']['aankomststation'])) {
            $this->request->data['Experience']['vertrekstation'] = $this->GetStation->getStationId($this->request->data['Experience']['vertrekstation']);
            $this->request->data['Experience']['aankomststation'] = $this->GetStation->getStationId($this->request->data['Experience']['aankomststation']);
        }
        return true;
    }

它应该返回站点名称的 ID。但在数据库中,存储的是名称本身(由用户填写)而不是 ID。我需要在我的组件中更改什么(我猜......)才能返回正确的值?

(PS组件中的查询本身返回一个ID,因为起初我将'beforeSave'直接放入保存数据的函数中,但后来我的验证错误说它不是正确的值。这是正确的...)

4

3 回答 3

1

补充其他答案;要仅获取单个字段的值,请使用Model::field()

return $stationInstance->field('id', $conditions);

最好在此语句中添加排序顺序,以确保始终以相同的顺序返回结果:

return $stationInstance->field('id', $conditions, 'code ASC');

由于您只对模型执行一次查询,之后不执行任何操作,因此您甚至不需要中间$stationInstance变量。您的代码可以进一步简化为:

return ClassRegistry::init('Station')->field('id', $conditions, 'code ASC');

一些观察

  • 由于电台名称的“模糊”匹配,第一个结果可能并不总是用户想要的电台,最好在前端提供“自动完成”功能并让用户选择正确的电台(例如,为了防止Den Haag在用户想要 Den Haag HS的时候选择)
  • 如果电台与电台不完全匹配,您应该发出未找到电台的警告
  • %对于查询,您没有围绕您的搜索词LIKE。如果您打算搜索 'name "contains",您应该使用'%' . $station . '%'. 对于“开始于”使用$station . '%'
  • 正如@mark 所建议的那样;beforeSave()模型的回调,应该位于那里。还; beforeSave()在验证发生后触发,因此可能为时已晚。beforeValidate()是最好的回调
  • 如果Experience模型已经附加到Station模型上,则不需要使用组件,因为您可以直接访问Station模型。最好将 search-method 放在Station模型中;

将其全部移动到右侧(*)位置

*) 其他选项总是可能的,这只是一种可能的方法

将“搜索”方法添加到 Station-model;

应用程序/模型/Station.php

public function getStationIdByName($name)
{
    $name = trim($name);

    if (empty($name)) {
        return null;
    }

    $name = '%' . $name . '%';

    $conditions = array(
        'OR' => array(
            array($this->alias . '.code LIKE' => $name),
            array($this->alias . '.naam LIKE' => $name),
            array($this->alias . '.naam_overig LIKE' => $name),
        )
    );

    return $this->field('id', $conditions, 'code ASC');
}

..并在体验模型中使用它

应用程序/模型/Experience.php

public function beforeValidate(array $options = array())
{
    if (
        !empty($this->data[$this->alias]['vertrekstation'])
        && !empty($this->data[$this->alias]['aankomststation'])
    ) {
        // Directly access the Station-model from within the Experience Model
        $this->data[$this->alias]['vertrekstation'] 
            = $this->Station->getStationIdByName($this->data[$this->alias]['vertrekstation']);
        $this->data[$this->alias]['aankomststation']
            = $this->Station->getStationIdByName($this->data[$this->alias]['aankomststation']);
    }

    // Call parent-callback after setting the values
    return parent::beforeValidate($options);
}

[更新] 使用约定,防止不必要的行为

写完前面的例子后,我注意到你当前的设置有一些缺陷;

  1. 如果vertrekstationaankomststation应该持有站点的“外键”(站点ID),则它们不是根据CakePHP 模型和数据库约定命名的
  2. 因为1)通过把这段代码放在里面,更新现有记录beforeValidate()时也会触发。因为您使用aankomststationvertrekstation字段来保存电台的名称(在表单内)和id (在数据库内),所以模型将在更新时尝试通过id查找 station-id 。请注意,表单中,您仍将使用vertrekstationaankomstation作为字段名称。这些字段名称不是存在于您的数据库中,因此将无法直接更新数据库中的数据,这就是beforeValidate()回调的用途
  3. 因为Experience模型需要与模型的两个关系Station(一次为departure station( 'vertrekstation' ),一次为arrival station( 'aankomststation' )),所以您需要 Station-model 的别名。请参阅:与同一模型的多个关系

应用程序/模型/Experience.php

class Experience extends AppModel {
    /**
     * Station will be associated to the 'Experience' Model TWICE
     * For clarity, using an 'alias' for both associations
     *
     * The associated Models will be accessible via;
     * $this->DepartureStation
     * $this->ArrivalStation
     *
     * To stick to the CakePHP conventions, name the foreign keys
     * accordingly
     */
    public $belongsTo = array(
        'DepartureStation' => array(
            'className'  => 'Station',
            'foreignKey' => 'departure_station_id',
        ),
        'ArrivalStation' => array(
            'className'  => 'Station',
            'foreignKey' => 'arrival_station_id',
        )
    );

    public function beforeValidate(array $options = array())
    {
        // vertrekstation and aankomststation hold the 'names' of the
        // stations and will only be present if the form has been submitted
        if (
            !empty($this->data[$this->alias]['vertrekstation'])
            && !empty($this->data[$this->alias]['aankomststation'])
        ) {
            // Directly access the Station-model from within the Experience Model
            // using the *aliases*
            $this->data[$this->alias]['departure_station_id'] 
                = $this->DepartureStation->getStationIdByName($this->data[$this->alias]['vertrekstation']);
            $this->data[$this->alias]['arrival_station_id']
                = $this->ArrivalStation->getStationIdByName($this->data[$this->alias]['aankomststation']);

            // Invalidate the vertrekstation and aankomststation fields if lookup failed
            if (empty($this->data[$this->alias]['departure_station_id'])) {
                // Unable to find a station. Mark the Form-field invalid
                $this->invalidate('vertrekstation', __('A station with this name was not found'));
            }
            if (empty($this->data[$this->alias]['arrival_station_id'])) {
                // Unable to find a station. Mark the Form-field invalid
                $this->invalidate('aankomststation', __('A station with this name was not found'));
            }

        }

        // Call parent-callback after setting the values
        return parent::beforeValidate($options);
    }

}
于 2013-04-05T07:00:37.897 回答
0

Cake 的 find('list') 选项返回一个数组,如

array( 1 => 'name1',
       3 => 'name2',
       //etc...
)

其中 index 是 id,value 是您在模型上设置的显示字段。因此,当您这样做时$value = array_values($result);,您将提取数组的值(即显示字段)。我假设您没有使用 id 作为 displayField,所以这就是它返回名称而不是 id 的原因。

我不确定您为什么使用 find('list') 而不是 find('first') 或其他替代方法,但是如果您出于某种原因不想修改它,则应该返回第一个 id 的修复搜索得到的是

reset($result);  //possibly not needed, but just in case
$value = key($result );
于 2013-04-04T19:09:31.027 回答
0

首先,您必须了解 Cake 的工作原理$this->request。您的模型中没有。它是控制器的一部分。在您的模型中,您传递的数据将$this->data直接存在。

public function beforeSave($options = array()) {
    parent::beforeSave($options); // you also forgot the parent call

    if (!empty($this->data[$this->alias]['vertrekstation']) && ...)) {
        $this->data[$this->alias]['vertrekstation'] = ...;
    }
    return true;
}

你的 find 电话看起来也很糟糕。我不知道你想做什么。但我强烈建议您使用 debug() 等来找出返回的内容并相应地更正您的代码。如果您只对单个值感兴趣,则可能需要 find(first) 。

于 2013-04-04T19:19:22.193 回答