CakePHP 3 autocomplete

W poprzednich dwóch częściach omawialiśmy dodawanie powiązanych rekordów. Ale co jeśli chcesz wybrać rekord z powiązanej tabeli, w której znajdują się tysiące rekordów? Funkcja autouzupełniania może pomóc w rozwiązaniu tego problemu. W kontrolerze użytkowników (src/Controller/UsersController.php) utwórz funkcję, która zwraca listę rekordów w formacie json na podstawie danych wejściowych do pola formularza.

public function getAll()
{
    if ($this->requrest->is('ajax')) {
        $this->autoRender = false;
        $name = $this->request->query['term'];
        $results = $this->Users->find('all', [
            'conditions' => [ 'OR' => [
                'first_name LIKE' => $name . '%',
                'last_name LIKE' => $name . '%',
            ]]
        ]);
        $resultsArr = [];
        foreach ($results as $result) {
            $resultsArr[] = ['label' => $result['full_name'], 'value' => $result['id']];
        }
        echo json_encode($resultsArr);
    }
}
W sekcji nagłówkowej strony załącz jQuery i jQuery UI.
echo $this->Html->script('//code.jquery.com/jquery-1.10.2.js');
echo $this->Html->script('//code.jquery.com/ui/1.11.4/jquery-ui.js');
echo $this->Html->css('//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css');
Następnie w akcjach add i edit PhoneNumbers (/src/Template/PhoneNumbers/add.ctp), dodajmy skrypt do wywołania metody autocomplete() jQuery, która z kolei wywołuje funkcję getAll() pobierającą dane.
<?php use Cake\Routing\Router; ?>
...
    echo $this->Form->input('user_id', ['type' => 'text']);
...
<script>
    jQuery('#user-id').autocomplete({
        source:'<?php echo Router::url(array('controller' => 'Users', 'action' => 'getAll')); ?>',
        minLength: 3
    });
</script>
Teraz, gdy zaczniesz wpisywać imię w polu user_id, powinna pojawić się lista autouzupełniania umożliwiająca wybranie użytkownika z listy. Jednak po wybraniu nazwy użytkownika z listy autouzupełniania w polu pojawi się identyfikator użytkownika. Skorygujemy to w dalszej części tutoriala. Checemy wybierać nazwę użytkownika z listy autouzupełniania, ale checemy zapisywać w bazie danych identyfikator użytkownika. W powiązanym modelu, w naszym przypadku modelu numerów telefonów (/src/Model/Table/PhoneNumbersTable.php) dodajemy:
use Cake\Event\Event;
use ArrayObject;
use Cake\ORM\TableRegistry;
Następnie skonstruujemy funkcję beforeMarshal(), aby pobrać dane wejściowe user->full_name i przekonwertować je na user->id.
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
    if (isset($data['user_id'])) {
        $users = TableRegistry::get('Users');
        $query = $users->find()
            ->select(['id'])
            ->where(['full_name' => $data['user_id']])
            ->first();
        $data['user_id'] = $query['id'];
    }
}
I na koniec ustaw funkcję getAll() dla naszych użytkowników, aby używać full_name jako wartości oraz etykiety.
$resultsArr[] =['label' => $result['full_name'], 'value' => $result['full_name']];
W tym momencie formularz umożliwia wybranie i wyświetlenie pełnej nazwy, ale po wczytaniu numeru telefonu do edycji nadal wyświetla identyfikator użytkownika. W kontrolerze numerów telefonów (/src/Controller/PhoneNumbersController.php) upewnij się, że po otrzymaniu numeru telefonu do edycji "zawiera" informacje o użytkowniku.
public function edit($id = null)
{
    $phoneNumber = $this->PhoneNumbers->get($id, [
        'contain' => ['Users']
    ]);
    ...
Następnie w widoku /src/Template/PhoneNumbers/edit.ctp ustaw żądaną wartość dla pola, przesłaniając wartość domyślną.
$this->Form->input('user_id', ['type' => 'text', 'value' => $phoneNumber->user->full_name]);