Compare commits
304 Commits
Author | SHA1 | Date |
---|---|---|
Alexander Makarov | fb45bf2386 | 6 years ago |
Alexander Makarov | 86fbdcbe45 | 6 years ago |
Brandon Kelly | 1d5ae6de43 | 6 years ago |
Sergey Aksenov | 7f36d26f1c | 6 years ago |
Andrii Vasyliev | 08590596e5 | 6 years ago |
Erik Verheij | 6f19d3d4a9 | 6 years ago |
Paul Klimov | e28bf15754 | 6 years ago |
Andrii Vasyliev | d24d795785 | 6 years ago |
Andrii Vasyliev | 22c3126c1c | 6 years ago |
Brandon Kelly | e1a49cfb6d | 6 years ago |
Mahesh S Warrier | f903fb2c0a | 6 years ago |
Alexander Makarov | 705f3e4a80 | 6 years ago |
Alexander Makarov | c184ef864e | 6 years ago |
cjtterabyte | 8cd14e7d54 | 6 years ago |
cjtterabyte | 1c2320fd32 | 6 years ago |
Alexander Makarov | 95e3237054 | 6 years ago |
cjtterabyte | 921447a25a | 6 years ago |
cjtterabyte | 9014f92bc5 | 6 years ago |
Alexander Makarov | 32efb09902 | 6 years ago |
Carsten Brandt | 501ddbeac3 | 6 years ago |
SilverFire - Dmitry Naumenko | be8633702d | 6 years ago |
SilverFire - Dmitry Naumenko | 1cc5f15635 | 6 years ago |
SilverFire - Dmitry Naumenko | fa71f808c1 | 6 years ago |
Kuts Dmitriy | 6b99e4f2e4 | 6 years ago |
SilverFire - Dmitry Naumenko | 2468282154 | 6 years ago |
SilverFire - Dmitry Naumenko | b9ffea141c | 6 years ago |
SilverFire - Dmitry Naumenko | 4f33296491 | 6 years ago |
SilverFire - Dmitry Naumenko | 3e4936982e | 6 years ago |
SilverFire - Dmitry Naumenko | e164c07145 | 6 years ago |
Carsten Brandt | 290621ce4e | 7 years ago |
Leandro Gehlen | 892f1a8711 | 7 years ago |
Leandro Gehlen | b576336beb | 7 years ago |
Pavel Ivanov | c12e665dc8 | 7 years ago |
sashsvamir | 115fa3f746 | 7 years ago |
Carsten Brandt | 418fc416df | 7 years ago |
Carsten Brandt | 7eadd66639 | 7 years ago |
Roman Zhuravlev | 9546bd8187 | 7 years ago |
Dmitry Naumenko | 8ecc361f2b | 7 years ago |
Robert Korulczyk | 8a46b33ef7 | 7 years ago |
Pavel Ivanov | 3e25001a67 | 7 years ago |
Pavel Ivanov | b0b06b1ad3 | 7 years ago |
Pavel Ivanov | 2c6bc8ff1c | 7 years ago |
Pavel Ivanov | 741a569695 | 7 years ago |
Pavel Ivanov | b88bb2b9f7 | 7 years ago |
Pavel Ivanov | a0b3e2bc15 | 7 years ago |
Pavel Ivanov | e4285c59f1 | 7 years ago |
Pavel Ivanov | a7f0fea200 | 7 years ago |
Pavel Ivanov | 20fdf81711 | 7 years ago |
Pavel Ivanov | 93a3260a3a | 7 years ago |
Pavel Ivanov | 0babc4b299 | 7 years ago |
Pavel Ivanov | 893f2a7bbd | 7 years ago |
Pavel Ivanov | f5315185dc | 7 years ago |
Pavel Ivanov | 4e15d33756 | 7 years ago |
Pavel Ivanov | 8005748714 | 7 years ago |
xicond | faac174af8 | 7 years ago |
Klimov Paul | fdd761d6cb | 7 years ago |
Tobias Munk | 25ffc7cefd | 7 years ago |
Klimov Paul | 43c8f457ba | 7 years ago |
Klimov Paul | 854ae7127c | 7 years ago |
Klimov Paul | 8ce498cf65 | 7 years ago |
Klimov Paul | e74fbb1b0f | 7 years ago |
Andrii Vasyliev | d7cf629b10 | 7 years ago |
Klimov Paul | d1ff652b2f | 7 years ago |
Klimov Paul | b04e0e06dc | 7 years ago |
Klimov Paul | 732d0a841f | 7 years ago |
Paul Klimov | 603c084fbc | 7 years ago |
Paul Klimov | 13c37091d5 | 7 years ago |
Paul Klimov | 39e9ba74bc | 7 years ago |
Paul Klimov | 7cf9116fe6 | 7 years ago |
Paul Klimov | 8779f7a187 | 7 years ago |
Paul Klimov | 0a5304419b | 7 years ago |
Paul Klimov | 5e2c16da66 | 7 years ago |
Paul Klimov | fafa4044c0 | 7 years ago |
Paul Klimov | 0cb91006d5 | 7 years ago |
Paul Klimov | 445c838fec | 7 years ago |
Paul Klimov | c68e553879 | 7 years ago |
Paul Klimov | bc77937bfd | 7 years ago |
Paul Klimov | 86abef49ff | 7 years ago |
Paul Klimov | 624d39c43e | 7 years ago |
Paul Klimov | f8893fe6c8 | 7 years ago |
Paul Klimov | 3b11ce91cb | 7 years ago |
Paul Klimov | 0f6dbdd135 | 7 years ago |
SilverFire - Dmitry Naumenko | 41aa5d07ba | 7 years ago |
Paul Klimov | ba73528038 | 7 years ago |
Paul Klimov | 56545c420a | 7 years ago |
Paul Klimov | a8d2127ad4 | 7 years ago |
Klimov Paul | 481970957b | 7 years ago |
Klimov Paul | fdad1c98c5 | 7 years ago |
Klimov Paul | e19d7a7917 | 7 years ago |
Klimov Paul | 8e39886f62 | 7 years ago |
Klimov Paul | e31dda7f22 | 7 years ago |
Klimov Paul | d5117984b3 | 7 years ago |
Paul Klimov | 9ef1c44749 | 7 years ago |
Paul Klimov | 0a7327f87e | 7 years ago |
Paul Klimov | d86b716b08 | 7 years ago |
Paul Klimov | bccd260e9c | 7 years ago |
Paul Klimov | 4c21603cb2 | 7 years ago |
Paul Klimov | c3d8a3ddb4 | 7 years ago |
Paul Klimov | b44a461c2e | 7 years ago |
Paul Klimov | a8b1f69d0c | 7 years ago |
Klimov Paul | c2694e7edc | 7 years ago |
Klimov Paul | 3b4412f195 | 7 years ago |
Alec Pritchard | 523df834b0 | 7 years ago |
Klimov Paul | b01a749afd | 7 years ago |
Klimov Paul | a39d1209ad | 7 years ago |
Klimov Paul | 29ce1e8687 | 7 years ago |
Nikolay Oleynikov | 21da0ed910 | 7 years ago |
Klimov Paul | 7171db4e74 | 7 years ago |
Klimov Paul | fcc12fef65 | 7 years ago |
Klimov Paul | e7da8b52a0 | 7 years ago |
Klimov Paul | f1a738ec07 | 7 years ago |
Klimov Paul | 13d443a68a | 7 years ago |
Alexander Makarov | d5f1627868 | 7 years ago |
Klimov Paul | 359175c04b | 7 years ago |
Klimov Paul | 9b86c46842 | 7 years ago |
Vladimir Khramtsov | 0391a367ed | 7 years ago |
Alexander Makarov | 61dec0f021 | 7 years ago |
Nikolay Oleynikov | fd9384fc00 | 7 years ago |
Dmitry Naumenko | 866c70d62e | 7 years ago |
Vladimir Khramtsov | 646cd73548 | 7 years ago |
Klimov Paul | da364fa419 | 7 years ago |
Klimov Paul | 7e3f096bc0 | 7 years ago |
Klimov Paul | 3666f2c957 | 7 years ago |
Klimov Paul | 3ff4eade2f | 7 years ago |
Paul Klimov | bf116e6103 | 7 years ago |
Klimov Paul | 75349fdeb3 | 7 years ago |
Klimov Paul | 796aed8d16 | 7 years ago |
Paul Klimov | 874fcaaecb | 7 years ago |
Klimov Paul | a21c9fd8eb | 7 years ago |
Alexander Makarov | 87fde26d6d | 7 years ago |
Alexander Makarov | 9a802dbf34 | 7 years ago |
Klimov Paul | dcc74c841a | 7 years ago |
Klimov Paul | ef3e89e36c | 7 years ago |
Klimov Paul | 2fb0b05a0f | 7 years ago |
Alexander Makarov | 45eed055cb | 7 years ago |
Alexander Makarov | 11ecfc54f8 | 7 years ago |
Alexander Makarov | b997fdace6 | 7 years ago |
Alexander Makarov | ac167cbd10 | 7 years ago |
Alexander Makarov | 742e7b81f6 | 7 years ago |
Alexander Makarov | 66b6e48f1a | 7 years ago |
Alexander Makarov | b62077d581 | 7 years ago |
Elvira Sheina | 7ce415b8c7 | 7 years ago |
E.Alamo | 34865a7eb0 | 7 years ago |
243083df | 08cafaa2ee | 7 years ago |
243083df | cac4462e7a | 7 years ago |
Klimov Paul | c421a121d2 | 7 years ago |
Klimov Paul | 2cfbc38bf8 | 7 years ago |
Paul Klimov | 497a073dfd | 7 years ago |
Paul Klimov | a136f414ec | 7 years ago |
Klimov Paul | d039605dd1 | 7 years ago |
Klimov Paul | 82ea6a86ee | 7 years ago |
Klimov Paul | 3ff59d37ee | 7 years ago |
Klimov Paul | fcff67548c | 7 years ago |
Klimov Paul | a5b9af951e | 7 years ago |
Klimov Paul | 4488a6d9af | 7 years ago |
Klimov Paul | 83fa82aceb | 7 years ago |
Klimov Paul | ee81a042cf | 7 years ago |
Paul Klimov | e0a7baf631 | 7 years ago |
Klimov Paul | 8854793834 | 7 years ago |
Klimov Paul | e7943971d2 | 7 years ago |
Paul Klimov | e703645b4d | 7 years ago |
Klimov Paul | 5c7bdb32a6 | 7 years ago |
Klimov Paul | 257604f19e | 7 years ago |
Klimov Paul | 37580db6b1 | 7 years ago |
Klimov Paul | 722022f744 | 7 years ago |
Alexander Makarov | 3c16faa5b0 | 7 years ago |
Klimov Paul | 2e01076abc | 7 years ago |
Klimov Paul | 89c14b7dea | 7 years ago |
Alexander Makarov | 2e267461a1 | 7 years ago |
Alexander Makarov | 4f9c642d74 | 7 years ago |
Paul Klimov | e82b9c72f3 | 7 years ago |
Alexander Makarov | dc42a59b71 | 7 years ago |
Klimov Paul | 08e5e1e71b | 7 years ago |
Klimov Paul | c39a1ba4c3 | 7 years ago |
Paul Klimov | 3e6f8b1c19 | 7 years ago |
Paul Klimov | 4d9204d9f3 | 7 years ago |
Paul Klimov | 28b26fb74b | 7 years ago |
Ruitang Du | 003d83c6e0 | 7 years ago |
Paul Klimov | a015795c7b | 7 years ago |
Paul Klimov | f590670ac9 | 7 years ago |
Boudewijn Vahrmeijer | 209bcf0773 | 7 years ago |
Boudewijn Vahrmeijer | 6605d9830b | 7 years ago |
Boudewijn Vahrmeijer | 31a405bf80 | 7 years ago |
Alexander Makarov | 42a7f2b012 | 7 years ago |
Alexander Makarov | cc869d2587 | 7 years ago |
Alexander Makarov | 2875624600 | 7 years ago |
Alexander Makarov | 0b421596a0 | 7 years ago |
Alexander Makarov | e52e1124c1 | 7 years ago |
Alexander Makarov | 1b45ed9691 | 7 years ago |
Alexander Makarov | a94e804ae4 | 7 years ago |
Alexander Makarov | adca091fbf | 7 years ago |
Alexander Makarov | fc47291b7e | 7 years ago |
Alexander Makarov | 85df384e8f | 7 years ago |
Alexander Makarov | ceaf101151 | 7 years ago |
Alexander Makarov | 6100194bf7 | 7 years ago |
Alexander Makarov | d4e7cf0171 | 7 years ago |
Alexander Makarov | 3e688c1a6b | 7 years ago |
Alexander Makarov | d98f0aff1a | 7 years ago |
Alexander Makarov | 0297c4e2aa | 7 years ago |
Alexander Makarov | 2a9d4efc45 | 7 years ago |
Alexander Makarov | 207f8cc0d5 | 7 years ago |
Alexander Makarov | 66095fb89a | 7 years ago |
Alexander Makarov | f41399a6dc | 7 years ago |
Alexander Makarov | 7ef2cf55b3 | 7 years ago |
Alexander Makarov | 75dda6e08a | 7 years ago |
Angel Guevara | 0117459444 | 7 years ago |
Alexander Makarov | 51a5d78c95 | 7 years ago |
Alexander Makarov | c3bca69b5b | 7 years ago |
Alexander Makarov | 8e7ebfdad4 | 7 years ago |
Alexander Makarov | add189e5ef | 7 years ago |
Alexander Makarov | 68543a63ff | 7 years ago |
Alexander Makarov | acef7e1e7c | 7 years ago |
Alexander Makarov | f475affaeb | 7 years ago |
Alexander Makarov | 0afc410d12 | 7 years ago |
Bob van Leeuwen | 7c85258c36 | 7 years ago |
刘旭 | e310618168 | 8 years ago |
Alexander Makarov | 7aeb008a5d | 8 years ago |
Alexander Makarov | 1f97e03b4f | 8 years ago |
Alexander Makarov | 68dd3d4567 | 8 years ago |
Klimov Paul | 07f65f411b | 8 years ago |
Klimov Paul | aca84c9a7a | 8 years ago |
Klimov Paul | 498ecb3b4f | 8 years ago |
Klimov Paul | bd989566ff | 8 years ago |
Klimov Paul | e3f26cc5dc | 8 years ago |
Alexander Makarov | f7a4838026 | 8 years ago |
Alexander Makarov | 0aca17dcfb | 8 years ago |
Alexander Makarov | 575609f21d | 8 years ago |
Alexander Makarov | a21c4a8dc0 | 8 years ago |
Alexander Makarov | a7b9ef650c | 8 years ago |
Alexander Makarov | 9eabc170ad | 8 years ago |
Alexander Makarov | 736fb099fb | 8 years ago |
Alexander Makarov | d6892d554a | 8 years ago |
Alexey Rogachev | 62932ef321 | 8 years ago |
Alexey Rogachev | 409c56c5c5 | 8 years ago |
SilverFire - Dmitry Naumenko | 8ea211771c | 8 years ago |
SilverFire - Dmitry Naumenko | 7a47f5f8c1 | 8 years ago |
SilverFire - Dmitry Naumenko | 511784631b | 8 years ago |
SilverFire - Dmitry Naumenko | 2ab2fd96cc | 8 years ago |
SilverFire - Dmitry Naumenko | 7f8fdf9d56 | 8 years ago |
SilverFire - Dmitry Naumenko | b063323547 | 8 years ago |
Carsten Brandt | 55a827dad9 | 8 years ago |
boehsermoe | c2cd20ee49 | 8 years ago |
SilverFire - Dmitry Naumenko | a560db1a8f | 8 years ago |
SilverFire - Dmitry Naumenko | c64cfa9373 | 8 years ago |
SilverFire - Dmitry Naumenko | fcba5f3171 | 8 years ago |
SilverFire - Dmitry Naumenko | a94c2e611d | 8 years ago |
SilverFire - Dmitry Naumenko | a8be03f2b1 | 8 years ago |
SilverFire - Dmitry Naumenko | e45f5c3b4f | 8 years ago |
SilverFire - Dmitry Naumenko | 76e6a20cee | 8 years ago |
SilverFire - Dmitry Naumenko | 1e24d9a92e | 8 years ago |
SilverFire - Dmitry Naumenko | d335fd6ea3 | 8 years ago |
Michael Härtl | 224aac83ab | 8 years ago |
Dmitry Naumenko | 164f35afef | 8 years ago |
Robert Korulczyk | c597518378 | 8 years ago |
Robert Korulczyk | 41f5981561 | 8 years ago |
Robert Korulczyk | a77749a6a1 | 8 years ago |
Yordan Ivanov | 0d72e37f3c | 8 years ago |
Alexander Makarov | 2f53cee566 | 8 years ago |
Alexander Makarov | 46e68bf9ec | 8 years ago |
Dmitry Naumenko | 78224615ad | 8 years ago |
Boudewijn Vahrmeijer | 410ceab3ae | 8 years ago |
Dmitry Naumenko | eea58f3462 | 8 years ago |
Brandon Kelly | e94a46d720 | 8 years ago |
Alexander Makarov | b5c2407c1d | 8 years ago |
Alexander Makarov | f3478bb59b | 8 years ago |
Alexander Makarov | b53430f959 | 8 years ago |
Alexander Makarov | 2722b3a7cf | 8 years ago |
Alexander Makarov | 653362e6f8 | 8 years ago |
Alexander Makarov | f8cf1e2b6a | 8 years ago |
Carsten Brandt | 6791fdf1c4 | 8 years ago |
Carsten Brandt | aeadab5012 | 8 years ago |
Carsten Brandt | 8c03e6753b | 8 years ago |
Alexander Makarov | b764b42f51 | 8 years ago |
Carsten Brandt | fcfd54a28c | 8 years ago |
Carsten Brandt | 9b54694bb0 | 8 years ago |
Carsten Brandt | 98ed490c3d | 8 years ago |
Kirill Pomerantsev | 7f61cc81d8 | 8 years ago |
Alexander Makarov | c952e8052b | 8 years ago |
Alexander Makarov | 5ebdf6c29a | 8 years ago |
Lennart van den Dool | 30286387db | 8 years ago |
Lennart van den Dool | 79bdc5eb15 | 8 years ago |
Alexander Makarov | f8358acaeb | 8 years ago |
Alexander Makarov | a10365a3db | 8 years ago |
Carsten Brandt | 04949e1ab4 | 8 years ago |
Angel Guevara | f978199fcc | 9 years ago |
Angel Guevara | 35d7cacfbd | 9 years ago |
Angel Guevara | 0dee3509a8 | 9 years ago |
Carsten Brandt | 82d36df284 | 9 years ago |
Angel Guevara | e880ff2ff6 | 9 years ago |
Carsten Brandt | 25ac762d8c | 9 years ago |
Carsten Brandt | 9e830b1fbf | 9 years ago |
Carsten Brandt | b41e720bf8 | 9 years ago |
Carsten Brandt | 9cf7ffa186 | 9 years ago |
Carsten Brandt | 636591d284 | 9 years ago |
Carsten Brandt | 0888d6967b | 9 years ago |
Carsten Brandt | b503dc9ff2 | 9 years ago |
Carsten Brandt | 88a4f4ae9f | 9 years ago |
Alexander Makarov | c5efe32c28 | 9 years ago |
Alexander Makarov | 52d70732c1 | 9 years ago |
Alexander Makarov | 64e134c29c | 9 years ago |
Alexander Makarov | 6e947c84be | 9 years ago |
Alexander Makarov | 9b1a87b776 | 9 years ago |
Alexander Makarov | b05e01b2d4 | 9 years ago |
Alexander Makarov | 083db9792d | 9 years ago |
1462 changed files with 24355 additions and 80827 deletions
@ -1,5 +0,0 @@ |
|||||||
# These are supported funding model platforms |
|
||||||
|
|
||||||
open_collective: yiisoft |
|
||||||
github: [yiisoft] |
|
||||||
tidelift: "packagist/yiisoft/yii2" |
|
@ -1,6 +0,0 @@ |
|||||||
# Security Policy |
|
||||||
|
|
||||||
Please use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you find in Yii. |
|
||||||
DO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help. |
|
||||||
|
|
||||||
Please note that as a non-commercial OpenSource project we are not able to pay bounties at the moment. |
|
@ -1,113 +0,0 @@ |
|||||||
name: build |
|
||||||
|
|
||||||
on: [push, pull_request] |
|
||||||
|
|
||||||
env: |
|
||||||
DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi" |
|
||||||
PHPUNIT_EXCLUDE_GROUP: mssql,oci,wincache,xcache,zenddata,cubrid |
|
||||||
XDEBUG_MODE: coverage, develop |
|
||||||
|
|
||||||
jobs: |
|
||||||
phpunit: |
|
||||||
name: PHP ${{ matrix.php }} on ${{ matrix.os }} |
|
||||||
runs-on: ${{ matrix.os }} |
|
||||||
services: |
|
||||||
mysql: |
|
||||||
image: mysql:5.7 |
|
||||||
env: |
|
||||||
MYSQL_ROOT_PASSWORD: root |
|
||||||
MYSQL_DATABASE: yiitest |
|
||||||
ports: |
|
||||||
- 3306:3306 |
|
||||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 |
|
||||||
postgres: |
|
||||||
image: postgres:9.6 |
|
||||||
env: |
|
||||||
POSTGRES_USER: postgres |
|
||||||
POSTGRES_PASSWORD: postgres |
|
||||||
POSTGRES_DB: yiitest |
|
||||||
ports: |
|
||||||
- 5432:5432 |
|
||||||
options: --name=postgres --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 |
|
||||||
strategy: |
|
||||||
fail-fast: false |
|
||||||
matrix: |
|
||||||
os: [ubuntu-latest] |
|
||||||
php: ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Generate french locale |
|
||||||
run: sudo locale-gen fr_FR.UTF-8 |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2 |
|
||||||
- name: Install PHP |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php }} |
|
||||||
tools: pecl |
|
||||||
extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached, mysql, pdo, pdo_mysql, pdo_pgsql, pdo_sqlite, pgsql, sqlite |
|
||||||
ini-values: date.timezone='UTC', session.save_path="${{ runner.temp }}" |
|
||||||
- name: Install Memcached |
|
||||||
uses: niden/actions-memcached@v7 |
|
||||||
- name: Get composer cache directory |
|
||||||
id: composer-cache |
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)" |
|
||||||
- name: Cache composer dependencies |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ steps.composer-cache.outputs.dir }} |
|
||||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} |
|
||||||
restore-keys: ${{ runner.os }}-composer- |
|
||||||
- name: Install dependencies |
|
||||||
run: composer update $DEFAULT_COMPOSER_FLAGS |
|
||||||
- name: PHP Unit tests for PHP 7.1 |
|
||||||
run: vendor/bin/phpunit --verbose --coverage-clover=coverage.clover --exclude-group $PHPUNIT_EXCLUDE_GROUP --colors=always |
|
||||||
if: matrix.php == '7.1' |
|
||||||
- name: PHP Unit tests for PHP >= 7.2 |
|
||||||
run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --colors=always |
|
||||||
env: |
|
||||||
PHPUNIT_EXCLUDE_GROUP: mssql,oci,wincache,xcache,zenddata,cubrid |
|
||||||
if: matrix.php >= '7.2' |
|
||||||
- name: PHP Unit tests for PHP <= 7.0 |
|
||||||
run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --colors=always |
|
||||||
if: matrix.php <= '7.0' |
|
||||||
- name: Code coverage |
|
||||||
run: | |
|
||||||
wget https://scrutinizer-ci.com/ocular.phar |
|
||||||
php ocular.phar code-coverage:upload --format=php-clover coverage.clover |
|
||||||
if: matrix.php == '7.1' |
|
||||||
continue-on-error: true # if is fork |
|
||||||
|
|
||||||
npm: |
|
||||||
name: NPM 6 on ubuntu-latest |
|
||||||
runs-on: ubuntu-latest |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2 |
|
||||||
- name: Install PHP |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: 7.2 |
|
||||||
ini-values: session.save_path=${{ runner.temp }} |
|
||||||
- name: Get composer cache directory |
|
||||||
id: composer-cache |
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)" |
|
||||||
- name: Cache composer dependencies |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ steps.composer-cache.outputs.dir }} |
|
||||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} |
|
||||||
restore-keys: ${{ runner.os }}-composer- |
|
||||||
- name: Install dependencies |
|
||||||
run: composer update $DEFAULT_COMPOSER_FLAGS |
|
||||||
- name: Install node.js |
|
||||||
uses: actions/setup-node@v1 |
|
||||||
with: |
|
||||||
node-version: 6 |
|
||||||
- name: Tests |
|
||||||
run: | |
|
||||||
npm install |
|
||||||
npm test |
|
||||||
# env: |
|
||||||
# CI: true |
|
@ -1,103 +0,0 @@ |
|||||||
on: |
|
||||||
- pull_request |
|
||||||
- push |
|
||||||
|
|
||||||
name: ci-mssql |
|
||||||
|
|
||||||
jobs: |
|
||||||
tests: |
|
||||||
name: PHP ${{ matrix.php }}-mssql-${{ matrix.mssql }} |
|
||||||
|
|
||||||
env: |
|
||||||
key: cache |
|
||||||
|
|
||||||
runs-on: ubuntu-latest |
|
||||||
|
|
||||||
strategy: |
|
||||||
matrix: |
|
||||||
include: |
|
||||||
- php: '7.0' |
|
||||||
extensions: pdo, pdo_sqlsrv-5.8.1 |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '7.1' |
|
||||||
extensions: pdo, pdo_sqlsrv-5.8.1 |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '7.2' |
|
||||||
extensions: pdo, pdo_sqlsrv-5.8.1 |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '7.3' |
|
||||||
extensions: pdo, pdo_sqlsrv-5.8.1 |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '7.4' |
|
||||||
extensions: pdo, pdo_sqlsrv |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '7.4' |
|
||||||
extensions: pdo, pdo_sqlsrv |
|
||||||
mssql: 'server:2019-latest' |
|
||||||
- php: '8.0' |
|
||||||
extensions: pdo, pdo_sqlsrv |
|
||||||
mssql: 'server:2017-latest' |
|
||||||
- php: '8.0' |
|
||||||
extensions: pdo, pdo_sqlsrv |
|
||||||
mssql: 'server:2019-latest' |
|
||||||
|
|
||||||
services: |
|
||||||
mssql: |
|
||||||
image: mcr.microsoft.com/mssql/${{ matrix.mssql }} |
|
||||||
env: |
|
||||||
SA_PASSWORD: YourStrong!Passw0rd |
|
||||||
ACCEPT_EULA: Y |
|
||||||
MSSQL_PID: Developer |
|
||||||
ports: |
|
||||||
- 1433:1433 |
|
||||||
options: --name=mssql --health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'" --health-interval=10s --health-timeout=5s --health-retries=3 |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2.3.4 |
|
||||||
|
|
||||||
- name: Create MS SQL Database |
|
||||||
run: docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE yiitest' |
|
||||||
|
|
||||||
- name: Install PHP with extensions |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php }} |
|
||||||
extensions: ${{ matrix.extensions }} |
|
||||||
ini-values: date.timezone='UTC' |
|
||||||
tools: composer:v2, pecl |
|
||||||
|
|
||||||
- name: Determine composer cache directory on Linux |
|
||||||
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV |
|
||||||
|
|
||||||
- name: Cache dependencies installed with composer |
|
||||||
uses: actions/cache@v2 |
|
||||||
with: |
|
||||||
path: ${{ env.COMPOSER_CACHE_DIR }} |
|
||||||
key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} |
|
||||||
restore-keys: | |
|
||||||
php${{ matrix.php }}-composer- |
|
||||||
|
|
||||||
- name: Update composer |
|
||||||
run: composer self-update |
|
||||||
|
|
||||||
- name: Install dependencies with composer |
|
||||||
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi |
|
||||||
|
|
||||||
- name: Install dependencies with composer php 8.0 |
|
||||||
if: matrix.php == '8.0' |
|
||||||
run: composer update --ignore-platform-reqs --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi |
|
||||||
|
|
||||||
- name: PHP Unit tests for PHP 7.1 |
|
||||||
run: vendor/bin/phpunit --coverage-clover=coverage.clover --group mssql --colors=always |
|
||||||
if: matrix.php == '7.1' |
|
||||||
|
|
||||||
- name: Run tests with phpunit without coverage |
|
||||||
run: vendor/bin/phpunit --group mssql --colors=always |
|
||||||
|
|
||||||
- name: Code coverage |
|
||||||
run: | |
|
||||||
wget https://scrutinizer-ci.com/ocular.phar |
|
||||||
php ocular.phar code-coverage:upload --format=php-clover coverage.clover |
|
||||||
if: matrix.php == '7.1' |
|
||||||
continue-on-error: true # if is fork |
|
@ -1,80 +0,0 @@ |
|||||||
on: |
|
||||||
- pull_request |
|
||||||
- push |
|
||||||
|
|
||||||
name: ci-mysql |
|
||||||
|
|
||||||
jobs: |
|
||||||
tests: |
|
||||||
name: PHP ${{ matrix.php-version }}-mysql-${{ matrix.mysql-version }} |
|
||||||
env: |
|
||||||
extensions: curl, intl, pdo, pdo_mysql |
|
||||||
key: cache-v1 |
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }} |
|
||||||
|
|
||||||
strategy: |
|
||||||
matrix: |
|
||||||
os: |
|
||||||
- ubuntu-latest |
|
||||||
|
|
||||||
php-version: |
|
||||||
- "7.4" |
|
||||||
|
|
||||||
mysql-version: |
|
||||||
- "latest" |
|
||||||
|
|
||||||
services: |
|
||||||
mysql: |
|
||||||
image: mysql:${{ matrix.mysql-version }} |
|
||||||
env: |
|
||||||
MYSQL_ROOT_PASSWORD: root |
|
||||||
MYSQL_DATABASE: yiitest |
|
||||||
ports: |
|
||||||
- 3306:3306 |
|
||||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2 |
|
||||||
|
|
||||||
- name: Setup cache environment |
|
||||||
id: cache-env |
|
||||||
uses: shivammathur/cache-extensions@v1 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php-version }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
key: ${{ env.key }} |
|
||||||
|
|
||||||
- name: Cache extensions |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ steps.cache-env.outputs.dir }} |
|
||||||
key: ${{ steps.cache-env.outputs.key }} |
|
||||||
restore-keys: ${{ steps.cache-env.outputs.key }} |
|
||||||
|
|
||||||
- name: Install PHP with extensions |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php-version }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
ini-values: date.timezone='UTC' |
|
||||||
coverage: pcov |
|
||||||
|
|
||||||
- name: Determine composer cache directory |
|
||||||
if: matrix.os == 'ubuntu-latest' |
|
||||||
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV |
|
||||||
|
|
||||||
- name: Cache dependencies installed with composer |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ env.COMPOSER_CACHE_DIR }} |
|
||||||
key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }} |
|
||||||
restore-keys: | |
|
||||||
php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- |
|
||||||
|
|
||||||
- name: Install dependencies with composer |
|
||||||
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi |
|
||||||
|
|
||||||
- name: Run mysql tests with phpunit |
|
||||||
run: vendor/bin/phpunit --group mysql --colors=always |
|
@ -1,81 +0,0 @@ |
|||||||
on: |
|
||||||
- pull_request |
|
||||||
- push |
|
||||||
|
|
||||||
name: ci-oracle |
|
||||||
|
|
||||||
jobs: |
|
||||||
tests: |
|
||||||
name: PHP ${{ matrix.php }}-${{ matrix.os }} |
|
||||||
|
|
||||||
env: |
|
||||||
extensions: oci8, pdo, pdo_oci |
|
||||||
key: cache-v1 |
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }} |
|
||||||
|
|
||||||
strategy: |
|
||||||
matrix: |
|
||||||
os: |
|
||||||
- ubuntu-latest |
|
||||||
|
|
||||||
php: |
|
||||||
- "7.4" |
|
||||||
|
|
||||||
services: |
|
||||||
oci: |
|
||||||
image: wnameless/oracle-xe-11g-r2:latest |
|
||||||
ports: |
|
||||||
- 1521:1521 |
|
||||||
options: --name=oci |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2 |
|
||||||
|
|
||||||
- name: Setup cache environment |
|
||||||
id: cache-env |
|
||||||
uses: shivammathur/cache-extensions@v1 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
key: ${{ env.key }} |
|
||||||
|
|
||||||
- name: Cache extensions |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ steps.cache-env.outputs.dir }} |
|
||||||
key: ${{ steps.cache-env.outputs.key }} |
|
||||||
restore-keys: ${{ steps.cache-env.outputs.key }} |
|
||||||
|
|
||||||
- name: Install PHP with extensions |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
ini-values: date.timezone='UTC' |
|
||||||
coverage: pcov |
|
||||||
tools: composer:v2, pecl |
|
||||||
|
|
||||||
- name: Determine composer cache directory |
|
||||||
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV |
|
||||||
|
|
||||||
- name: Cache dependencies installed with composer |
|
||||||
uses: actions/cache@v2 |
|
||||||
with: |
|
||||||
path: ${{ env.COMPOSER_CACHE_DIR }} |
|
||||||
key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} |
|
||||||
restore-keys: | |
|
||||||
php${{ matrix.php }}-composer- |
|
||||||
|
|
||||||
- name: Install dependencies with composer |
|
||||||
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi |
|
||||||
|
|
||||||
- name: PHP Unit tests |
|
||||||
run: vendor/bin/phpunit --coverage-clover=coverage.clover --group oci --colors=always |
|
||||||
|
|
||||||
- name: Code coverage |
|
||||||
run: | |
|
||||||
wget https://scrutinizer-ci.com/ocular.phar |
|
||||||
php ocular.phar code-coverage:upload --format=php-clover coverage.clover |
|
||||||
continue-on-error: true # if is fork |
|
@ -1,84 +0,0 @@ |
|||||||
on: |
|
||||||
- pull_request |
|
||||||
- push |
|
||||||
|
|
||||||
name: ci-pgsql |
|
||||||
|
|
||||||
jobs: |
|
||||||
tests: |
|
||||||
name: PHP ${{ matrix.php-version }}-pgsql-${{ matrix.pgsql-version }} |
|
||||||
env: |
|
||||||
extensions: curl, intl, pdo, pdo_pgsql |
|
||||||
key: cache-v1 |
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }} |
|
||||||
|
|
||||||
strategy: |
|
||||||
matrix: |
|
||||||
os: |
|
||||||
- ubuntu-latest |
|
||||||
|
|
||||||
php-version: |
|
||||||
- "7.4" |
|
||||||
|
|
||||||
pgsql-version: |
|
||||||
- "10" |
|
||||||
- "11" |
|
||||||
- "12" |
|
||||||
- "13" |
|
||||||
|
|
||||||
services: |
|
||||||
postgres: |
|
||||||
image: postgres:${{ matrix.pgsql-version }} |
|
||||||
env: |
|
||||||
POSTGRES_USER: postgres |
|
||||||
POSTGRES_PASSWORD: postgres |
|
||||||
POSTGRES_DB: yiitest |
|
||||||
ports: |
|
||||||
- 5432:5432 |
|
||||||
options: --name=postgres --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 |
|
||||||
|
|
||||||
steps: |
|
||||||
- name: Checkout |
|
||||||
uses: actions/checkout@v2 |
|
||||||
|
|
||||||
- name: Setup cache environment |
|
||||||
id: cache-env |
|
||||||
uses: shivammathur/cache-extensions@v1 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php-version }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
key: ${{ env.key }} |
|
||||||
|
|
||||||
- name: Cache extensions |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ steps.cache-env.outputs.dir }} |
|
||||||
key: ${{ steps.cache-env.outputs.key }} |
|
||||||
restore-keys: ${{ steps.cache-env.outputs.key }} |
|
||||||
|
|
||||||
- name: Install PHP with extensions |
|
||||||
uses: shivammathur/setup-php@v2 |
|
||||||
with: |
|
||||||
php-version: ${{ matrix.php-version }} |
|
||||||
extensions: ${{ env.extensions }} |
|
||||||
ini-values: date.timezone='UTC' |
|
||||||
coverage: pcov |
|
||||||
|
|
||||||
- name: Determine composer cache directory |
|
||||||
if: matrix.os == 'ubuntu-latest' |
|
||||||
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV |
|
||||||
|
|
||||||
- name: Cache dependencies installed with composer |
|
||||||
uses: actions/cache@v1 |
|
||||||
with: |
|
||||||
path: ${{ env.COMPOSER_CACHE_DIR }} |
|
||||||
key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }} |
|
||||||
restore-keys: | |
|
||||||
php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- |
|
||||||
|
|
||||||
- name: Install dependencies with composer |
|
||||||
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi |
|
||||||
|
|
||||||
- name: Run pgsql tests with phpunit |
|
||||||
run: vendor/bin/phpunit --group pgsql --colors=always |
|
@ -1,20 +1,10 @@ |
|||||||
build: |
imports: |
||||||
nodes: |
- php |
||||||
analysis: |
|
||||||
tests: |
|
||||||
override: |
|
||||||
- php-scrutinizer-run |
|
||||||
|
|
||||||
filter: |
|
||||||
excluded_paths: |
|
||||||
- "tests/" |
|
||||||
- "build/" |
|
||||||
- "docs/" |
|
||||||
- "framework/messages/" |
|
||||||
dependency_paths: |
|
||||||
- "vendor/" |
|
||||||
|
|
||||||
tools: |
tools: |
||||||
external_code_coverage: |
external_code_coverage: |
||||||
runs: 3 |
|
||||||
timeout: 2100 # Timeout in seconds. |
timeout: 2100 # Timeout in seconds. |
||||||
|
# disable copy paste detector and similarity analyzer as they have no real value |
||||||
|
# and a huge bunch of false-positives |
||||||
|
php_sim: false |
||||||
|
php_cpd: false |
||||||
|
@ -0,0 +1,149 @@ |
|||||||
|
# |
||||||
|
# Travis Setup |
||||||
|
# |
||||||
|
|
||||||
|
# use ubuntu trusty for newer version of nodejs, used for JS testing |
||||||
|
dist: trusty |
||||||
|
|
||||||
|
# faster builds on new travis setup not using sudo |
||||||
|
# temporary disable, see https://github.com/travis-ci/travis-ci/issues/6842 |
||||||
|
#sudo: false |
||||||
|
sudo: required |
||||||
|
group: edge |
||||||
|
|
||||||
|
# build only on master branches |
||||||
|
# commented as this prevents people from running builds on their forks: |
||||||
|
# https://github.com/yiisoft/yii2/commit/bd87be990fa238c6d5e326d0a171f38d02dc253a |
||||||
|
#branches: |
||||||
|
# only: |
||||||
|
# - master |
||||||
|
# - 3.0 |
||||||
|
|
||||||
|
|
||||||
|
# |
||||||
|
# Test Matrix |
||||||
|
# |
||||||
|
|
||||||
|
language: php |
||||||
|
|
||||||
|
env: |
||||||
|
global: |
||||||
|
- DEFAULT_COMPOSER_FLAGS="--prefer-dist --no-interaction --no-progress --optimize-autoloader" |
||||||
|
- TASK_TESTS_PHP=1 |
||||||
|
- TASK_TESTS_COVERAGE=0 |
||||||
|
- TRAVIS_SECOND_USER=travis_two |
||||||
|
|
||||||
|
|
||||||
|
services: |
||||||
|
- memcached |
||||||
|
- mysql |
||||||
|
- postgresql |
||||||
|
|
||||||
|
# cache vendor dirs |
||||||
|
cache: |
||||||
|
directories: |
||||||
|
- vendor |
||||||
|
- $HOME/.composer/cache |
||||||
|
- $HOME/.npm |
||||||
|
|
||||||
|
# try running against postgres 9.6 |
||||||
|
addons: |
||||||
|
postgresql: "9.6" |
||||||
|
apt: |
||||||
|
sources: |
||||||
|
- mysql-5.7-trusty |
||||||
|
packages: |
||||||
|
- mysql-server |
||||||
|
code_climate: |
||||||
|
repo_token: 2935307212620b0e2228ab67eadd92c9f5501ddb60549d0d86007a354d56915b |
||||||
|
|
||||||
|
matrix: |
||||||
|
fast_finish: true |
||||||
|
include: |
||||||
|
- php: 7.2 |
||||||
|
|
||||||
|
# run tests coverage on PHP 7.1 |
||||||
|
- php: 7.1 |
||||||
|
env: TASK_TESTS_COVERAGE=1 |
||||||
|
- php: nightly |
||||||
|
services: |
||||||
|
- mysql |
||||||
|
- postgresql |
||||||
|
|
||||||
|
allow_failures: |
||||||
|
- php: nightly |
||||||
|
|
||||||
|
install: |
||||||
|
- | |
||||||
|
if [[ $TASK_TESTS_COVERAGE != 1 ]]; then |
||||||
|
# disable xdebug for performance reasons when code coverage is not needed |
||||||
|
phpenv config-rm xdebug.ini || echo "xdebug is not installed" |
||||||
|
fi |
||||||
|
|
||||||
|
# install composer dependencies |
||||||
|
- travis_retry composer self-update |
||||||
|
- export PATH="$HOME/.composer/vendor/bin:$PATH" |
||||||
|
- travis_retry composer install $DEFAULT_COMPOSER_FLAGS |
||||||
|
|
||||||
|
# setup PHP extension |
||||||
|
- | |
||||||
|
if [[ $TASK_TESTS_PHP == 1 && $TRAVIS_PHP_VERSION != nightly ]]; then |
||||||
|
tests/data/travis/apcu-setup.sh |
||||||
|
tests/data/travis/memcache-setup.sh |
||||||
|
tests/data/travis/imagick-setup.sh |
||||||
|
fi |
||||||
|
|
||||||
|
# Needed for FileCacheTest |
||||||
|
- sudo useradd $TRAVIS_SECOND_USER --gid $(id -g) -M |
||||||
|
|
||||||
|
before_script: |
||||||
|
# show some versions and env information |
||||||
|
- php --version |
||||||
|
- composer --version |
||||||
|
- | |
||||||
|
if [ $TASK_TESTS_PHP == 1 ]; then |
||||||
|
php -r "echo INTL_ICU_VERSION . \"\n\";" |
||||||
|
php -r "echo INTL_ICU_DATA_VERSION . \"\n\";" |
||||||
|
psql --version |
||||||
|
mysql --version |
||||||
|
fi |
||||||
|
|
||||||
|
# initialize databases |
||||||
|
- | |
||||||
|
if [ $TASK_TESTS_PHP == 1 ]; then |
||||||
|
travis_retry mysql -e 'CREATE DATABASE `yiitest`;'; |
||||||
|
mysql -e "SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';"; |
||||||
|
mysql -e "CREATE USER 'travis'@'localhost' IDENTIFIED WITH mysql_native_password;"; |
||||||
|
mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'localhost' WITH GRANT OPTION;"; |
||||||
|
psql -U postgres -c 'CREATE DATABASE yiitest;'; |
||||||
|
fi |
||||||
|
|
||||||
|
# enable code coverage |
||||||
|
- | |
||||||
|
if [ $TASK_TESTS_COVERAGE == 1 ]; then |
||||||
|
PHPUNIT_FLAGS="--coverage-clover=coverage.clover" |
||||||
|
fi |
||||||
|
|
||||||
|
# Disable DEPRECATE messages during PHPUnit initialization on PHP 7.2. To fix them, PHPUnit should be updated to 6.* |
||||||
|
# For Yii2 tests, messages will be enabled by tests/bootstrap.php |
||||||
|
- | |
||||||
|
if [[ $TRAVIS_PHP_VERSION == 7.2 || $TRAVIS_PHP_VERSION = nightly ]]; then |
||||||
|
echo 'Disabled DEPRECATED notifications for PHP >= 7.2'; |
||||||
|
echo 'error_reporting = E_ALL & ~E_DEPRECATED' >> /tmp/php-config.ini; |
||||||
|
phpenv config-add /tmp/php-config.ini; |
||||||
|
fi |
||||||
|
|
||||||
|
|
||||||
|
script: |
||||||
|
# PHP tests |
||||||
|
- | |
||||||
|
if [ $TASK_TESTS_PHP == 1 ]; then |
||||||
|
vendor/bin/phpunit --verbose $PHPUNIT_FLAGS --exclude-group wincache,xcache |
||||||
|
fi |
||||||
|
|
||||||
|
after_script: |
||||||
|
- | |
||||||
|
if [ $TASK_TESTS_COVERAGE == 1 ]; then |
||||||
|
travis_retry wget https://scrutinizer-ci.com/ocular.phar |
||||||
|
php ocular.phar code-coverage:upload --format=php-clover coverage.clover |
||||||
|
fi |
@ -1,27 +0,0 @@ |
|||||||
> Roadmap for Yii 3.0 and further was moved to [yiisoft/docs](https://github.com/yiisoft/docs/blob/master/003-roadmap.md). |
|
||||||
|
|
||||||
- Enhancements are not accepted for framework version 2.0. |
|
||||||
- Enhancements are accepted for 2.0 extensions. |
|
||||||
- Bug and security fixes are expected. |
|
||||||
- Pull requests and maintainers are very welcome. |
|
||||||
|
|
||||||
Above would stand as it is [for two years after Yii 3.0 release](https://www.yiiframework.com/release-cycle). |
|
||||||
|
|
||||||
## Additional releases |
|
||||||
|
|
||||||
While we focus on 3.0, we tag 2.0 releases and extension releases [about once in a week](https://www.yiiframework.com/release-cycle). |
|
||||||
|
|
||||||
|
|
||||||
## 2.0.16+ (since 2019 till 3.0 release + 2 years) |
|
||||||
|
|
||||||
- Bugfixes. |
|
||||||
|
|
||||||
## 2.0.15 (2nd quarter of 2018) |
|
||||||
|
|
||||||
- Since this release main focus is bug fixing. |
|
||||||
- No full-branch merges into 3.0. |
|
||||||
- No enhancements are accepted. |
|
||||||
|
|
||||||
## 2.0.14 (1st quarter of 2018) |
|
||||||
|
|
||||||
Will be last release with features and enhancements the last one that will be merged into 3.0 directly. |
|
@ -1,205 +0,0 @@ |
|||||||
Yii 2.0 الدليل التقني الخاص ببيئة العمل |
|
||||||
=============================== |
|
||||||
|
|
||||||
تم تحرير هذا الملف اعتمادا على [الشروط الخاصة بتوثيف ال Yii](https://www.yiiframework.com/doc/terms/). |
|
||||||
|
|
||||||
جميع الحقوق محفوظة |
|
||||||
|
|
||||||
2014 (c) Yii Software LLC. |
|
||||||
|
|
||||||
|
|
||||||
المقدمة |
|
||||||
------------ |
|
||||||
|
|
||||||
* [عن بيئة العمل Yii](intro-yii.md) |
|
||||||
* [التحديث من الإصدار 1.1](../guide/intro-upgrade-from-v1.md) |
|
||||||
|
|
||||||
|
|
||||||
البداية من هنا |
|
||||||
--------------- |
|
||||||
|
|
||||||
* [ماذا يجب أن تعرف عن بيئة العمل](start-prerequisites.md) |
|
||||||
* [تثبيت ال Yii](start-installation.md) |
|
||||||
* [تشغيل التطبيقات - Running Applications](start-workflow.md) |
|
||||||
* [قل مرحبا - المشروع الأول](start-hello.md) |
|
||||||
* [التعامل مع ال forms](start-forms.md) |
|
||||||
* [التعامل مع قواعد البيانات](start-databases.md) |
|
||||||
* [إنشاء الشيفرة البرمجية من خلال ال gii](start-gii.md) |
|
||||||
* [ماذا الآن - الخطوة القادمة](start-looking-ahead.md) |
|
||||||
|
|
||||||
|
|
||||||
الهيكلية الخاصة بالتطبيق (Application Structure) |
|
||||||
--------------------- |
|
||||||
|
|
||||||
* [نظرة عامة عن الهيكلية الخاصة بالتطبيق](../guide/structure-overview.md) |
|
||||||
* [Entry Scripts](../guide/structure-entry-scripts.md) |
|
||||||
* [التطبيقات](../guide/structure-applications.md) |
|
||||||
* [مكونات التطبيقات](../guide/structure-application-components.md) |
|
||||||
* [Controllers](../guide/structure-controllers.md) |
|
||||||
* [Models](../guide/structure-models.md) |
|
||||||
* [Views](../guide/structure-views.md) |
|
||||||
* [Modules](../guide/structure-modules.md) |
|
||||||
* [Filters](../guide/structure-filters.md) |
|
||||||
* [Widgets](../guide/structure-widgets.md) |
|
||||||
* [Assets](../guide/structure-assets.md) |
|
||||||
* [Extensions](../guide/structure-extensions.md) |
|
||||||
|
|
||||||
|
|
||||||
التعامل مع ال requests |
|
||||||
----------------- |
|
||||||
|
|
||||||
* [نظرة عامة عن التعامل مع ال requests](../guide/runtime-overview.md) |
|
||||||
* [Bootstrapping](../guide/runtime-bootstrapping.md) |
|
||||||
* [Routing and URL Creation](../guide/runtime-routing.md) |
|
||||||
* [Requests](../guide/runtime-requests.md) |
|
||||||
* [Responses](../guide/runtime-responses.md) |
|
||||||
* [Sessions and Cookies](../guide/runtime-sessions-cookies.md) |
|
||||||
* [Handling Errors - التحكم بالأخطاء](../guide/runtime-handling-errors.md) |
|
||||||
* [Logging - تسجيل الحركات](../guide/runtime-logging.md) |
|
||||||
|
|
||||||
|
|
||||||
المفاهيم الرئيسية (Key Concepts) |
|
||||||
------------ |
|
||||||
|
|
||||||
* [Components](../guide/concept-components.md) |
|
||||||
* [Properties](../guide/concept-properties.md) |
|
||||||
* [Events](../guide/concept-events.md) |
|
||||||
* [Behaviors](../guide/concept-behaviors.md) |
|
||||||
* [Configurations](../guide/concept-configurations.md) |
|
||||||
* [Aliases](../guide/concept-aliases.md) |
|
||||||
* [Class Autoloading](../guide/concept-autoloading.md) |
|
||||||
* [Service Locator](../guide/concept-service-locator.md) |
|
||||||
* [Dependency Injection Container](../guide/concept-di-container.md) |
|
||||||
|
|
||||||
|
|
||||||
التعامل مع قواعد البيانات |
|
||||||
---------------------- |
|
||||||
|
|
||||||
* [Database Access Objects](../guide/db-dao.md): Connecting to a database, basic queries, transactions, and schema manipulation |
|
||||||
* [Query Builder](../guide/db-query-builder.md): Querying the database using a simple abstraction layer |
|
||||||
* [Active Record](../guide/db-active-record.md): The Active Record ORM, retrieving and manipulating records, and defining relations |
|
||||||
* [Migrations](../guide/db-migrations.md): Apply version control to your databases in a team development environment |
|
||||||
* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide) |
|
||||||
* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide) |
|
||||||
* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide) |
|
||||||
* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide) |
|
||||||
|
|
||||||
|
|
||||||
الحصول على البيانات من خلال المستخدمين |
|
||||||
----------------------- |
|
||||||
|
|
||||||
* [Creating Forms](../guide/input-forms.md) |
|
||||||
* [Validating Input](../guide/input-validation.md) |
|
||||||
* [Uploading Files](../guide/input-file-upload.md) |
|
||||||
* [Collecting Tabular Input](../guide/input-tabular-input.md) |
|
||||||
* [Getting Data for Multiple Models](../guide/input-multiple-models.md) |
|
||||||
* [Extending ActiveForm on the Client Side](../guide/input-form-javascript.md) |
|
||||||
|
|
||||||
|
|
||||||
عرض البيانات |
|
||||||
--------------- |
|
||||||
|
|
||||||
* [Data Formatting](../guide/output-formatting.md) |
|
||||||
* [Pagination](../guide/output-pagination.md) |
|
||||||
* [Sorting](../guide/output-sorting.md) |
|
||||||
* [Data Providers](../guide/output-data-providers.md) |
|
||||||
* [Data Widgets](../guide/output-data-widgets.md) |
|
||||||
* [Working with Client Scripts](../guide/output-client-scripts.md) |
|
||||||
* [Theming](../guide/output-theming.md) |
|
||||||
|
|
||||||
|
|
||||||
الامان والحماية |
|
||||||
-------- |
|
||||||
|
|
||||||
* [Security Overview](../guide/security-overview.md) |
|
||||||
* [Authentication](../guide/security-authentication.md) |
|
||||||
* [Authorization](../guide/security-authorization.md) |
|
||||||
* [Working with Passwords](../guide/security-passwords.md) |
|
||||||
* [Cryptography](../guide/security-cryptography.md) |
|
||||||
* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide) |
|
||||||
* [Best Practices](../guide/security-best-practices.md) |
|
||||||
|
|
||||||
|
|
||||||
Caching التخزين المؤقت |
|
||||||
------- |
|
||||||
|
|
||||||
* [Caching Overview](../guide/caching-overview.md) |
|
||||||
* [Data Caching](../guide/caching-data.md) |
|
||||||
* [Fragment Caching](../guide/caching-fragment.md) |
|
||||||
* [Page Caching](../guide/caching-page.md) |
|
||||||
* [HTTP Caching](../guide/caching-http.md) |
|
||||||
|
|
||||||
|
|
||||||
RESTful Web Services |
|
||||||
-------------------- |
|
||||||
|
|
||||||
* [Quick Start](../guide/rest-quick-start.md) |
|
||||||
* [Resources](../guide/rest-resources.md) |
|
||||||
* [Controllers](../guide/rest-controllers.md) |
|
||||||
* [Routing](../guide/rest-routing.md) |
|
||||||
* [Response Formatting](../guide/rest-response-formatting.md) |
|
||||||
* [Authentication](../guide/rest-authentication.md) |
|
||||||
* [Rate Limiting](../guide/rest-rate-limiting.md) |
|
||||||
* [Versioning](../guide/rest-versioning.md) |
|
||||||
* [Error Handling](../guide/rest-error-handling.md) |
|
||||||
|
|
||||||
|
|
||||||
الأدوات المساعدة أثناء تطوير التطبيقات |
|
||||||
----------------- |
|
||||||
|
|
||||||
* [Debug Toolbar and Debugger](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide) |
|
||||||
* [Generating Code using Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) |
|
||||||
* [Generating API Documentation](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc) |
|
||||||
|
|
||||||
|
|
||||||
فحص واختبار التطبيقات |
|
||||||
------- |
|
||||||
|
|
||||||
* [Testing Overview](../guide/test-overview.md) |
|
||||||
* [Testing environment setup](../guide/test-environment-setup.md) |
|
||||||
* [Unit Tests](../guide/test-unit.md) |
|
||||||
* [Functional Tests](../guide/test-functional.md) |
|
||||||
* [Acceptance Tests](../guide/test-acceptance.md) |
|
||||||
* [Fixtures](../guide/test-fixtures.md) |
|
||||||
|
|
||||||
|
|
||||||
مواضيع وعناوين مميزة |
|
||||||
-------------- |
|
||||||
|
|
||||||
* [Advanced Project Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) |
|
||||||
* [Building Application from Scratch](../guide/tutorial-start-from-scratch.md) |
|
||||||
* [Console Commands](../guide/tutorial-console.md) |
|
||||||
* [Core Validators](../guide/tutorial-core-validators.md) |
|
||||||
* [Docker](../guide/tutorial-docker.md) |
|
||||||
* [Internationalization](../guide/tutorial-i18n.md) |
|
||||||
* [Mailing](../guide/tutorial-mailing.md) |
|
||||||
* [Performance Tuning](../guide/tutorial-performance-tuning.md) |
|
||||||
* [Shared Hosting Environment](../guide/tutorial-shared-hosting.md) |
|
||||||
* [Template Engines](../guide/tutorial-template-engines.md) |
|
||||||
* [Working with Third-Party Code](../guide/tutorial-yii-integration.md) |
|
||||||
* [Using Yii as a micro framework](../guide/tutorial-yii-as-micro-framework.md) |
|
||||||
|
|
||||||
|
|
||||||
Widgets |
|
||||||
------- |
|
||||||
|
|
||||||
* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html) |
|
||||||
* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html) |
|
||||||
* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html) |
|
||||||
* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform) |
|
||||||
* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html) |
|
||||||
* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html) |
|
||||||
* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html) |
|
||||||
* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html) |
|
||||||
* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide) |
|
||||||
* [jQuery UI Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide) |
|
||||||
|
|
||||||
|
|
||||||
Helpers |
|
||||||
------- |
|
||||||
|
|
||||||
* [Helpers Overview](../guide/helper-overview.md) |
|
||||||
* [ArrayHelper](../guide/helper-array.md) |
|
||||||
* [Html](../guide/helper-html.md) |
|
||||||
* [Url](../guide/helper-url.md) |
|
||||||
|
|
@ -1,36 +0,0 @@ |
|||||||
# <div dir="rtl">ماذا الآن - الخطوة القادمة</div> |
|
||||||
|
|
||||||
<p dir="rtl"> |
|
||||||
إذا قمت بقرائة الفصل الخاص ب "البداية من هنا"، فأنت الآن قادر على بناء تطبيق Yii متكامل، لقد تعلمت كيف يمكنك تنفيذ وإستخدام أكثر الخصائص والمميزات المشتركة، مثل جلب البيانات من قاعدة البيانات، وأخذ البيانات من المستخدمين، ومن ثم عرضها، كما تعلمت كيف يمكنك تقسيم البيانات الى صفحات، وقمنا باستخدام ال <a href="https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide">Gii</a> وتعلمنا كيف يمكننا إنشاء شيفرة برمجية من خلاله بشكل تلقائي، ومن الممارسة تعلمنا أن إنشاء الشيفرة البرمجية من خلال ال Gii يجعل من عملية تطوير المواقع والوظائف المطلوبة أمرا بسيطا وسهلا للغابة، كل ما عليك القيام به هو تعبئة ال forms. |
|
||||||
</p> |
|
||||||
|
|
||||||
<p dir="rtl"> |
|
||||||
في هذا الجزء من التوثيق سنعرض ملخص للمصادر المتاحة لل Yii، والتي ستساعدك في تحسين إنتاجيتك عند إستخدامك لبيئة العمل Yii. |
|
||||||
</p> |
|
||||||
|
|
||||||
<ul dir="rtl"> |
|
||||||
<li> |
|
||||||
التوثيق |
|
||||||
<ul> |
|
||||||
<li><a href="https://www.yiiframework.com/doc-2.0/guide-README.html">The Definitive Guide - الدليل الشامل</a>: كما يشير الإسم، فإن هذا الدليل يحدد آلية عمل ال Yii بدقة عالية، ويوفر إرشادات حول كيفية إستخدام ال Yii، هذا الجزء الأكثر أهمية في ال Yii، والذي يجب عليك قرائته قبل كتابة أي Yii code (ملاحظة: جزء البداية من هنا، والذي قمنا بدراسته هو أحد هذه الأجزاء، ومن أهمها للبدء بإنشاء التطبيقات من خلال ال Yii).</li> |
|
||||||
<li><a href="https://www.yiiframework.com/doc-2.0/index.html">The Class Reference - المرجع الخاص بال Class</a> في هذا الجزء يتم تحديد الية إستخدام كل Class يقدمه ال Yii، في العادة يتم إستخدام هذا المرجع عند كتابة شيفرة برمجية وأنت ترغب في فهم آلية العمل ل Class معين، أو Method, او فهم Proporty معينة...الخ، من الأفضل إستخدام المرجع الخاص بال Class فقط عند فهم آلية العمل لل Yii. </li> |
|
||||||
<li><a href="https://www.yiiframework.com/wiki/?tag=yii2">The Wiki Articles - مقالات الويكي</a> مقالات الويكي هي مجموعة من الخبرات العملية للمستخدمين، تمت كتابتها ونشرها على شكل مقالات لمشاركة الخبرات، ومعظم هذه الكتابات تكون مثل الوصفات الخاصة بالطبخ، موجودة لخدمة هدف معين، وحل مشكلة محددة باستخدام ال Yii، وبالرغم من أن هذه الكتابات قد لا تكون بجودة ودقة الدليل الشامل، الا أنها قد تغطي مواضيع أكثر، وتطرح أيضا حلولا مباشرة للإستخدام.</li> |
|
||||||
<li><a href="https://www.yiiframework.com/doc/">الكتب</a></li> |
|
||||||
</ul> |
|
||||||
</li> |
|
||||||
<li><a href="https://www.yiiframework.com/extensions/">Extensions - الملحقات</a>: تفتخر ال Yii بوجود مكتبة ضخمة من الملحقات التي تمت برمجتها وإضافتها من قبل المستخدمين المتطوعين الذين شاركوا أعمالهم وطورها لتجعل مهمة المطورين الآخرين أسهل وأسرع في تطوير التطبيفات المبنية بواسطة ال Yii.</li> |
|
||||||
<li> |
|
||||||
المجتمع |
|
||||||
<ul> |
|
||||||
<li>المنتدى: <a href="https://forum.yiiframework.com/">https://forum.yiiframework.com/</a></li> |
|
||||||
<li>IRC chat: The #yii channel on the Libera (ircs://irc.libera.chat:6697/yii)</li> |
|
||||||
<li>Slack chanel: <a href="https://yii.slack.com">https://yii.slack.com</a></li> |
|
||||||
<li>Gitter chat: <a href="https://gitter.im/yiisoft/yii2">https://gitter.im/yiisoft/yii2</a></li> |
|
||||||
<li>GitHub: <a href="https://github.com/yiisoft/yii2">https://github.com/yiisoft/yii2</a></li> |
|
||||||
<li>Facebook: <a href="https://www.facebook.com/groups/yiitalk/">https://www.facebook.com/groups/yiitalk/</a></li> |
|
||||||
<li>Twitter: <a href="https://twitter.com/yiiframework">https://twitter.com/yiiframework</a></li> |
|
||||||
<li>LinkedIn: <a href="https://www.linkedin.com/groups/yii-framework-1483367">https://www.linkedin.com/groups/yii-framework-1483367</a></li> |
|
||||||
<li>Stackoverflow: <a href="https://stackoverflow.com/questions/tagged/yii2">https://stackoverflow.com/questions/tagged/yii2</a></li> |
|
||||||
</ul> |
|
||||||
</li> |
|
||||||
</ul> |
|
@ -1,421 +0,0 @@ |
|||||||
Proveedores de datos |
|
||||||
==================== |
|
||||||
|
|
||||||
En las secciones sobre [paginación](output-pagination.md) y [ordenación](output-sorting.md) se |
|
||||||
describe como permitir a los usuarios finales elegir que se muestre una página de datos en |
|
||||||
particular, y ordenar los datos por algunas columnas. Como la tarea de paginar y ordenar datos |
|
||||||
es muy común, Yii proporciona un conjunto de clases *proveedoras de datos* para encapsularla. |
|
||||||
|
|
||||||
Un proveedor de datos es una clase que implementa la interfaz [[yii\data\DataProviderInterface]]. |
|
||||||
Básicamente se encarga de obtener datos paginados y ordenados. Normalmente se usa junto con |
|
||||||
[_widgets_ de datos](output-data-widgets.md) para que los usuarios finales puedan paginar y |
|
||||||
ordenar datos de forma interactiva. |
|
||||||
|
|
||||||
Yii incluye las siguientes clases proveedoras de datos: |
|
||||||
|
|
||||||
* [[yii\data\ActiveDataProvider]]: usa [[yii\db\Query]] o [[yii\db\ActiveQuery]] para consultar datos de bases de datos y devolverlos como _arrays_ o instancias [Active Record](db-active-record.md). |
|
||||||
* [[yii\data\SqlDataProvider]]: ejecuta una sentencia SQL y devuelve los datos de la base de datos como _arrays_. |
|
||||||
* [[yii\data\ArrayDataProvider]]: toma un _array_ grande y devuelve una rodaja de él basándose en las especificaciones de paginación y ordenación. |
|
||||||
|
|
||||||
El uso de todos estos proveedores de datos comparte el siguiente patrón común: |
|
||||||
|
|
||||||
```php |
|
||||||
// Crear el proveedor de datos configurando sus propiedades de paginación y ordenación |
|
||||||
$provider = new XyzDataProvider([ |
|
||||||
'pagination' => [...], |
|
||||||
'sort' => [...], |
|
||||||
]); |
|
||||||
|
|
||||||
// Obtener los datos paginados y ordenados |
|
||||||
$models = $provider->getModels(); |
|
||||||
|
|
||||||
// Obtener el número de elementos de la página actual |
|
||||||
$count = $provider->getCount(); |
|
||||||
|
|
||||||
// Obtener el número total de elementos entre todas las páginas |
|
||||||
$totalCount = $provider->getTotalCount(); |
|
||||||
``` |
|
||||||
|
|
||||||
Se puede especificar los comportamientos de paginación y ordenación de un proveedor de datos |
|
||||||
configurando sus propiedades [[yii\data\BaseDataProvider::pagination|pagination]] y |
|
||||||
[[yii\data\BaseDataProvider::sort|sort]], que corresponden a las configuraciones para |
|
||||||
[[yii\data\Pagination]] y [[yii\data\Sort]] respectivamente. También se pueden configurar a |
|
||||||
`false` para inhabilitar las funciones de paginación y/u ordenación. |
|
||||||
|
|
||||||
Los [_widgets_ de datos](output-data-widgets.md), como [[yii\grid\GridView]], tienen una |
|
||||||
propiedad llamada `dataProvider` que puede tomar una instancia de un proveedor de datos y |
|
||||||
mostrar los datos que proporciona. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
echo yii\grid\GridView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
Estos proveedores de datos varían principalmente en la manera en que se especifica la fuente de |
|
||||||
datos. En las siguientes secciones se explica el uso detallado de cada uno de estos proveedores |
|
||||||
de datos. |
|
||||||
|
|
||||||
|
|
||||||
## Proveedor de datos activo <span id="active-data-provider"></span> |
|
||||||
|
|
||||||
Para usar [[yii\data\ActiveDataProvider]], hay que configurar su propiedad |
|
||||||
[[yii\data\ActiveDataProvider::query|query]]. |
|
||||||
Puede tomar un objeto [[yii\db\Query] o [[yii\db\ActiveQuery]]. En el primer caso, los datos |
|
||||||
devueltos serán _arrays_. En el segundo, los datos devueltos pueden ser _arrays_ o instancias de |
|
||||||
[Active Record](db-active-record.md). Por ejemplo: |
|
||||||
|
|
||||||
|
|
||||||
```php |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
|
|
||||||
$query = Post::find()->where(['state_id' => 1]); |
|
||||||
|
|
||||||
$provider = new ActiveDataProvider([ |
|
||||||
'query' => $query, |
|
||||||
'pagination' => [ |
|
||||||
'pageSize' => 10, |
|
||||||
], |
|
||||||
'sort' => [ |
|
||||||
'defaultOrder' => [ |
|
||||||
'created_at' => SORT_DESC, |
|
||||||
'title' => SORT_ASC, |
|
||||||
] |
|
||||||
], |
|
||||||
]); |
|
||||||
|
|
||||||
// Devuelve un array de objetos Post |
|
||||||
$posts = $provider->getModels(); |
|
||||||
``` |
|
||||||
|
|
||||||
En el ejemplo anterior, si `$query` se crea el siguiente código, el proveedor de datos |
|
||||||
devolverá _arrays_ en bruto. |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\Query; |
|
||||||
|
|
||||||
$query = (new Query())->from('post')->where(['state' => 1]); |
|
||||||
``` |
|
||||||
|
|
||||||
> Note: Si una consulta ya tiene la cláusula `orderBy`, las nuevas instrucciones de ordenación |
|
||||||
dadas por los usuarios finales (mediante la configuración de `sort`) se añadirán a la cláusula |
|
||||||
`orderBy` previa. Las cláusulas `limit` y `offset` que pueda haber se sobrescribirán por la |
|
||||||
petición de paginación de los usuarios finales (mediante la configuración de `pagination`). |
|
||||||
|
|
||||||
Por omisión, [[yii\data\ActiveDataProvider]] usa el componente `db` de la aplicación como |
|
||||||
conexión con la base de datos. Se puede indicar una conexión con base de datos diferente |
|
||||||
configurando la propiedad [[yii\data\ActiveDataProvider::db]]. |
|
||||||
|
|
||||||
|
|
||||||
## Proveedor de datos SQL <span id="sql-data-provider"></span> |
|
||||||
|
|
||||||
[[yii\data\SqlDataProvider]] funciona con una sentencia SQL en bruto, que se usa para obtener |
|
||||||
los datos requeridos. |
|
||||||
Basándose en las especificaciones de [[yii\data\SqlDataProvider::sort|sort]] y |
|
||||||
[[yii\data\SqlDataProvider::pagination|pagination]], el proveedor ajustará las cláusulas |
|
||||||
`ORDER BY` y `LIMIT` de la sentencia SQL acordemente para obtener sólo la página de datos |
|
||||||
solicitados en el orden deseado. |
|
||||||
|
|
||||||
Para usar [[yii\data\SqlDataProvider]], hay que especificar las propiedades |
|
||||||
[[yii\data\SqlDataProvider::sql|sql]] y [[yii\data\SqlDataProvider::totalCount|totalCount]]. |
|
||||||
Por ejemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\data\SqlDataProvider; |
|
||||||
|
|
||||||
$count = Yii::$app->db->createCommand(' |
|
||||||
SELECT COUNT(*) FROM post WHERE status=:status |
|
||||||
', [':status' => 1])->queryScalar(); |
|
||||||
|
|
||||||
$provider = new SqlDataProvider([ |
|
||||||
'sql' => 'SELECT * FROM post WHERE status=:status', |
|
||||||
'params' => [':status' => 1], |
|
||||||
'totalCount' => $count, |
|
||||||
'pagination' => [ |
|
||||||
'pageSize' => 10, |
|
||||||
], |
|
||||||
'sort' => [ |
|
||||||
'attributes' => [ |
|
||||||
'title', |
|
||||||
'view_count', |
|
||||||
'created_at', |
|
||||||
], |
|
||||||
], |
|
||||||
]); |
|
||||||
|
|
||||||
// Devuelve un array de filas de datos |
|
||||||
$models = $provider->getModels(); |
|
||||||
``` |
|
||||||
|
|
||||||
> Info: La propiedad [[yii\data\SqlDataProvider::totalCount|totalCount]] se requiere sólo si se |
|
||||||
necesita paginar los datos. Esto es porque el proveedor modificará la sentencia SQL |
|
||||||
especificada vía [[yii\data\SqlDataProvider::sql|sql]] para que devuelva sólo la pagina de |
|
||||||
datos solicitada. El proveedor sigue necesitando saber el número total de elementos de datos |
|
||||||
para calcular correctamente el número de páginas. |
|
||||||
|
|
||||||
|
|
||||||
## Proveedor de datos de _arrays_ <span id="array-data-provider"></span> |
|
||||||
|
|
||||||
Se recomienda usar [[yii\data\ArrayDataProvider]] cuando se trabaja con un _array_ grande. |
|
||||||
El proveedor permite devolver una página de los datos del _array_ ordenados por una o varias |
|
||||||
columnas. Para usar [[yii\data\ArrayDataProvider]], hay que especificar la propiedad |
|
||||||
[[yii\data\ArrayDataProvider::allModels|allModels]] como el _array_ grande. Los elementos |
|
||||||
del _array_ grande pueden ser _arrays_ asociativos (por ejemplo resultados de consultas de |
|
||||||
[DAO](db-dao.md) u objetos (por ejemplo instancias de [Active Record](db-active-record.md). |
|
||||||
Por ejemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\data\ArrayDataProvider; |
|
||||||
|
|
||||||
$data = [ |
|
||||||
['id' => 1, 'name' => 'name 1', ...], |
|
||||||
['id' => 2, 'name' => 'name 2', ...], |
|
||||||
... |
|
||||||
['id' => 100, 'name' => 'name 100', ...], |
|
||||||
]; |
|
||||||
|
|
||||||
$provider = new ArrayDataProvider([ |
|
||||||
'allModels' => $data, |
|
||||||
'pagination' => [ |
|
||||||
'pageSize' => 10, |
|
||||||
], |
|
||||||
'sort' => [ |
|
||||||
'attributes' => ['id', 'name'], |
|
||||||
], |
|
||||||
]); |
|
||||||
|
|
||||||
// Obtener las filas de la página solicitada |
|
||||||
$rows = $provider->getModels(); |
|
||||||
``` |
|
||||||
|
|
||||||
> Note: En comparación con [Active Data Provider](#active-data-provider) y |
|
||||||
[SQL Data Provider](#sql-data-provider), Array Data Provider es menos eficiente porque |
|
||||||
requiere cargar *todos* los datos en memoria. |
|
||||||
|
|
||||||
|
|
||||||
## Trabajar con las claves de los datos <span id="working-with-keys"></span> |
|
||||||
|
|
||||||
Al utilizar los elementos de datos devueltos por un proveedor de datos, con frecuencia |
|
||||||
necesita identificar cada elemento de datos con una clave única. |
|
||||||
Por ejemplo, si los elementos de datos representan información de los clientes, puede querer |
|
||||||
usar el ID de cliente como la clave de cada conjunto de datos de un cliente. |
|
||||||
Los proveedores de datos pueden devolver una lista de estas claves correspondientes a los |
|
||||||
elementos de datos devueltos por [[yii\data\DataProviderInterface::getModels()]]. |
|
||||||
Por ejemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
|
|
||||||
$query = Post::find()->where(['status' => 1]); |
|
||||||
|
|
||||||
$provider = new ActiveDataProvider([ |
|
||||||
'query' => $query, |
|
||||||
]); |
|
||||||
|
|
||||||
// Devuelve un array de objetos Post |
|
||||||
$posts = $provider->getModels(); |
|
||||||
|
|
||||||
// Devuelve los valores de las claves primarias correspondientes a $posts |
|
||||||
$ids = $provider->getKeys(); |
|
||||||
``` |
|
||||||
|
|
||||||
En el ejemplo superior, como se le proporciona a [[yii\data\ActiveDataProvider]] un objeto |
|
||||||
[[yii\db\ActiveQuery]], es lo suficientemente inteligente como para devolver los valores de |
|
||||||
las claves primarias como las claves. También puede indicar explícitamente cómo se deben |
|
||||||
calcular los valores de la clave configurando [[yii\data\ActiveDataProvider::key]] con un |
|
||||||
nombre de columna o un invocable que calcule los valores de la clave. Por ejemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
// Utiliza la columna «slug» como valores de la clave |
|
||||||
$provider = new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
'key' => 'slug', |
|
||||||
]); |
|
||||||
|
|
||||||
// Utiliza el resultado de md5(id) como valores de la clave |
|
||||||
$provider = new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
'key' => function ($model) { |
|
||||||
return md5($model->id); |
|
||||||
} |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Creación de un proveedor de datos personalizado <span id="custom-data-provider"></span> |
|
||||||
|
|
||||||
Para crear su propio proveedor de datos personalizado, debe implementar |
|
||||||
[[yii\data\DataProviderInterface]]. |
|
||||||
Una manera más fácil es extender [[yii\data\BaseDataProvider]], que le permite centrarse |
|
||||||
en la lógica central del proveedor de datos. En particular, esencialmente necesita |
|
||||||
implementar los siguientes métodos: |
|
||||||
|
|
||||||
- [[yii\data\BaseDataProvider::prepareModels()|prepareModels()]]: prepara los modelos |
|
||||||
de datos que estarán disponibles en la página actual y los devuelve como un _array_. |
|
||||||
- [[yii\data\BaseDataProvider::prepareKeys()|prepareKeys()]]: acepta un _array_ de |
|
||||||
modelos de datos disponibles actualmente y devuelve las claves asociadas a ellos. |
|
||||||
- [[yii\data\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: devuelve un valor |
|
||||||
que indica el número total de modelos de datos en el proveedor de datos. |
|
||||||
|
|
||||||
Debajo se muestra un ejemplo de un proveedor de datos que lee datos CSV eficientemente: |
|
||||||
|
|
||||||
```php |
|
||||||
<?php |
|
||||||
use yii\data\BaseDataProvider; |
|
||||||
|
|
||||||
class CsvDataProvider extends BaseDataProvider |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @var string nombre del fichero CSV a leer |
|
||||||
*/ |
|
||||||
public $filename; |
|
||||||
|
|
||||||
/** |
|
||||||
* @var string|callable nombre de la columna clave o un invocable que la devuelva |
|
||||||
*/ |
|
||||||
public $key; |
|
||||||
|
|
||||||
/** |
|
||||||
* @var SplFileObject |
|
||||||
*/ |
|
||||||
protected $fileObject; // SplFileObject es muy práctico para buscar una línea concreta en un fichero |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function init() |
|
||||||
{ |
|
||||||
parent::init(); |
|
||||||
|
|
||||||
// Abrir el fichero |
|
||||||
$this->fileObject = new SplFileObject($this->filename); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
protected function prepareModels() |
|
||||||
{ |
|
||||||
$models = []; |
|
||||||
$pagination = $this->getPagination(); |
|
||||||
|
|
||||||
if ($pagination === false) { |
|
||||||
// En caso de que no haya paginación, leer todas las líneas |
|
||||||
while (!$this->fileObject->eof()) { |
|
||||||
$models[] = $this->fileObject->fgetcsv(); |
|
||||||
$this->fileObject->next(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
// En caso de que haya paginación, leer sólo una única página |
|
||||||
$pagination->totalCount = $this->getTotalCount(); |
|
||||||
$this->fileObject->seek($pagination->getOffset()); |
|
||||||
$limit = $pagination->getLimit(); |
|
||||||
|
|
||||||
for ($count = 0; $count < $limit; ++$count) { |
|
||||||
$models[] = $this->fileObject->fgetcsv(); |
|
||||||
$this->fileObject->next(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $models; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
protected function prepareKeys($models) |
|
||||||
{ |
|
||||||
if ($this->key !== null) { |
|
||||||
$keys = []; |
|
||||||
|
|
||||||
foreach ($models as $model) { |
|
||||||
if (is_string($this->key)) { |
|
||||||
$keys[] = $model[$this->key]; |
|
||||||
} else { |
|
||||||
$keys[] = call_user_func($this->key, $model); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $keys; |
|
||||||
} |
|
||||||
|
|
||||||
return array_keys($models); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
protected function prepareTotalCount() |
|
||||||
{ |
|
||||||
$count = 0; |
|
||||||
|
|
||||||
while (!$this->fileObject->eof()) { |
|
||||||
$this->fileObject->next(); |
|
||||||
++$count; |
|
||||||
} |
|
||||||
|
|
||||||
return $count; |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
## Filtrar proveedores de datos usando filtros de datos <span id="filtering-data-providers-using-data-filters"></span> |
|
||||||
|
|
||||||
Si bien puede construir condiciones para un proveedor de datos activo manualmente tal |
|
||||||
y como se describe en las secciones [Filtering Data](output-data-widgets.md#filtering-data) |
|
||||||
y [Separate Filter Form](output-data-widgets.md#separate-filter-form) de la guía de |
|
||||||
_widgets_ de datos, Yii tiene filtros de datos que son muy útiles si necesita |
|
||||||
condiciones de filtro flexibles. Los filtros de datos se pueden usar así: |
|
||||||
|
|
||||||
```php |
|
||||||
$filter = new ActiveDataFilter([ |
|
||||||
'searchModel' => 'app\models\PostSearch' |
|
||||||
]); |
|
||||||
|
|
||||||
$filterCondition = null; |
|
||||||
|
|
||||||
// Puede cargar los filtros de datos de cualquier fuente. |
|
||||||
// Por ejemplo, si prefiere JSON en el cuerpo de la petición, |
|
||||||
// use Yii::$app->request->getBodyParams() aquí abajo: |
|
||||||
if ($filter->load(\Yii::$app->request->get())) { |
|
||||||
$filterCondition = $filter->build(); |
|
||||||
if ($filterCondition === false) { |
|
||||||
// Serializer recibiría errores |
|
||||||
return $filter; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$query = Post::find(); |
|
||||||
if ($filterCondition !== null) { |
|
||||||
$query->andWhere($filterCondition); |
|
||||||
} |
|
||||||
|
|
||||||
return new ActiveDataProvider([ |
|
||||||
'query' => $query, |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
El propósito del modelo `PostSearch` es definir por qué propiedades y valores se permite filtrar: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\base\Model; |
|
||||||
|
|
||||||
class PostSearch extends Model |
|
||||||
{ |
|
||||||
public $id; |
|
||||||
public $title; |
|
||||||
|
|
||||||
public function rules() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
['id', 'integer'], |
|
||||||
['title', 'string', 'min' => 2, 'max' => 200], |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Los filtros de datos son bastante flexibles. Puede personalizar cómo se construyen |
|
||||||
las condiciones y qué operadores se permiten. |
|
||||||
Para más detalles consulte la documentación de la API en [[\yii\data\DataFilter]]. |
|
@ -1,245 +0,0 @@ |
|||||||
Widgets de datos |
|
||||||
================ |
|
||||||
|
|
||||||
Yii proporciona un conjunto de [widgets](structure-widgets.md) que se pueden usar para mostrar datos. |
|
||||||
Mientras que el _widget_ [DetailView](#detail-view) se puede usar para mostrar los datos de un único |
|
||||||
registro, [ListView](#list-view) y [GridView](#grid-view) se pueden usar para mostrar una lista o |
|
||||||
tabla de registros de datos proporcionando funcionalidades como paginación, ordenación y filtro. |
|
||||||
|
|
||||||
|
|
||||||
DetailView <span id="detail-view"></span> |
|
||||||
---------- |
|
||||||
|
|
||||||
El _widget_ [[yii\widgets\DetailView|DetailView]] muestra los detalles de un único |
|
||||||
[[yii\widgets\DetailView::$model|modelo]] de datos. |
|
||||||
|
|
||||||
Se recomienda su uso para mostrar un modelo en un formato estándar (por ejemplo, cada atributo del |
|
||||||
modelo se muestra como una fila en una tabla). El modelo puede ser tanto una instancia o subclase |
|
||||||
de [[\yii\base\Model]] como un [active record](db-active-record.md) o un _array_ asociativo. |
|
||||||
|
|
||||||
DetailView usa la propiedad [[yii\widgets\DetailView::$attributes|$attributes]] para determinar |
|
||||||
qué atributos del modelo se deben mostrar y cómo se deben formatear. |
|
||||||
En la [sección sobre formateadores](output-formatting.html) se pueden ver las opciones de formato |
|
||||||
disponibles. |
|
||||||
|
|
||||||
Un uso típico de DetailView sería así: |
|
||||||
|
|
||||||
```php |
|
||||||
echo DetailView::widget([ |
|
||||||
'model' => $model, |
|
||||||
'attributes' => [ |
|
||||||
'title', // atributo title (en texto plano) |
|
||||||
'description:html', // atributo description formateado como HTML |
|
||||||
[ // nombre del propietario del modelo |
|
||||||
'label' => 'Owner', |
|
||||||
'value' => $model->owner->name, |
|
||||||
'contentOptions' => ['class' => 'bg-red'], // atributos HTML para personalizar el valor |
|
||||||
'captionOptions' => ['tooltip' => 'Tooltip'], // atributos HTML para personalizar la etiqueta |
|
||||||
], |
|
||||||
'created_at:datetime', // fecha de creación formateada como datetime |
|
||||||
], |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
Recuerde que a diferencia de [[yii\widgets\GridView|GridView]], que procesa un conjunto de modelos, |
|
||||||
[[yii\widgets\DetailView|DetailView]] sólo procesa uno. Así que la mayoría de las veces no hay |
|
||||||
necesidad de usar funciones anónimas ya que `$model` es el único modelo a mostrar y está disponible |
|
||||||
en la vista como una variable. |
|
||||||
|
|
||||||
Sin embargo, en algunos casos el uso de una función anónima puede ser útil. Por ejemplo cuando |
|
||||||
`visible` está especificado y se desea impedir el cálculo de `value` en case de que evalúe a `false`: |
|
||||||
|
|
||||||
```php |
|
||||||
echo DetailView::widget([ |
|
||||||
'model' => $model, |
|
||||||
'attributes' => [ |
|
||||||
[ |
|
||||||
'attribute' => 'owner', |
|
||||||
'value' => function ($model) { |
|
||||||
return $model->owner->name; |
|
||||||
}, |
|
||||||
'visible' => \Yii::$app->user->can('posts.owner.view'), |
|
||||||
], |
|
||||||
], |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
ListView <span id="list-view"></span> |
|
||||||
-------- |
|
||||||
|
|
||||||
El _widget_ [[yii\widgets\ListView|ListView]] se usa para mostrar datos de un |
|
||||||
[proveedor de datos](output-data-providers.md). |
|
||||||
Cada modelo de datos se representa usando el [[yii\widgets\ListView::$itemView|fichero de vista]] |
|
||||||
indicado. |
|
||||||
Como proporciona de serie funcionalidades tales como paginación, ordenación y filtro, |
|
||||||
es útil tanto para mostrar información al usuario final como para crear una interfaz |
|
||||||
de usuario de gestión de datos. |
|
||||||
|
|
||||||
Un uso típico es el siguiente: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\widgets\ListView; |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
|
|
||||||
$dataProvider = new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
'pagination' => [ |
|
||||||
'pageSize' => 20, |
|
||||||
], |
|
||||||
]); |
|
||||||
|
|
||||||
echo ListView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
'itemView' => '_post', |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
El fichero de vista `_post` podría contener lo siguiente: |
|
||||||
|
|
||||||
```php |
|
||||||
<?php |
|
||||||
use yii\helpers\Html; |
|
||||||
use yii\helpers\HtmlPurifier; |
|
||||||
?> |
|
||||||
<div class="tarea"> |
|
||||||
<h2><?= Html::encode($model->title) ?></h2> |
|
||||||
|
|
||||||
<?= HtmlPurifier::process($model->text) ?> |
|
||||||
</div> |
|
||||||
``` |
|
||||||
|
|
||||||
En el fichero de vista anterior, el modelo de datos actual está disponible como `$model`. |
|
||||||
Además están disponibles las siguientes variables: |
|
||||||
|
|
||||||
- `$key`: mixto, el valor de la clave asociada a este elemento de datos. |
|
||||||
- `$index`: entero, el índice empezando por cero del elemento de datos en el array de elementos devuelto por el proveedor de datos. |
|
||||||
- `$widget`: ListView, esta instancia del _widget_. |
|
||||||
|
|
||||||
Si se necesita pasar datos adicionales a cada vista, se puede usar la propiedad |
|
||||||
[[yii\widgets\ListView::$viewParams|$viewParams]] para pasar parejas clave-valor como las siguientes: |
|
||||||
|
|
||||||
```php |
|
||||||
echo ListView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
'itemView' => '_post', |
|
||||||
'viewParams' => [ |
|
||||||
'fullView' => true, |
|
||||||
'context' => 'main-page', |
|
||||||
// ... |
|
||||||
], |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
Entonces éstas también estarán disponibles en la vista como variables. |
|
||||||
|
|
||||||
|
|
||||||
GridView <span id="grid-view"></span> |
|
||||||
-------- |
|
||||||
|
|
||||||
La cuadrícula de datos o [[yii\grid\GridView|GridView]] es uno de los _widgets_ de Yii |
|
||||||
más potentes. Es extremadamente útil si necesita construir rápidamente la sección de |
|
||||||
administración del sistema. Recibe los datos de un [proveedor de datos](output-data-providers.md) |
|
||||||
y representa cada fila usando un conjunto de [[yii\grid\GridView::columns|columnas]] |
|
||||||
que presentan los datos en forma de tabla. |
|
||||||
|
|
||||||
Cada fila de la tabla representa los datos de un único elemento de datos, y una columna |
|
||||||
normalmente representa un atributo del elemento (algunas columnas pueden corresponder a |
|
||||||
expresiones complejas de los atributos o a un texto estático). |
|
||||||
|
|
||||||
El mínimo código necesario para usar GridView es como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\grid\GridView; |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
|
|
||||||
$dataProvider = new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
'pagination' => [ |
|
||||||
'pageSize' => 20, |
|
||||||
], |
|
||||||
]); |
|
||||||
echo GridView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
El código anterior primero crea un proveedor de datos y a continuación usa GridView |
|
||||||
para mostrar cada atributo en cada fila tomados del proveedor de datos. La tabla |
|
||||||
mostrada está equipada de serie con las funcionalidades de ordenación y paginación. |
|
||||||
|
|
||||||
|
|
||||||
### Columnas de la cuadrícula <span id="grid-columns"></span> |
|
||||||
|
|
||||||
Las columnas de la tabla se configuran en términos de clase [[yii\grid\Column]], que |
|
||||||
se configuran en la propiedad [[yii\grid\GridView::columns|columns]] de la configuración |
|
||||||
del GridView. |
|
||||||
Dependiendo del tipo y ajustes de las columnas éstas pueden presentar los datos de |
|
||||||
diferentes maneras. |
|
||||||
La clase predefinida es [[yii\grid\DataColumn]], que representa un atributo del modelo |
|
||||||
por el que se puede ordenar y filtrar. |
|
||||||
|
|
||||||
|
|
||||||
```php |
|
||||||
echo GridView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
'columns' => [ |
|
||||||
['class' => 'yii\grid\SerialColumn'], |
|
||||||
// Columnas sencillas definidas por los datos contenidos en $dataProvider. |
|
||||||
// Se usarán los datos de la columna del modelo. |
|
||||||
'id', |
|
||||||
'username', |
|
||||||
// Un ejemplo más complejo. |
|
||||||
[ |
|
||||||
'class' => 'yii\grid\DataColumn', // Se puede omitir, ya que es la predefinida. |
|
||||||
'value' => function ($data) { |
|
||||||
return $data->name; // $data['name'] para datos de un array, por ejemplo al usar SqlDataProvider. |
|
||||||
}, |
|
||||||
], |
|
||||||
], |
|
||||||
]); |
|
||||||
``` |
|
||||||
|
|
||||||
Observe que si no se especifica la parte [[yii\grid\GridView::columns|columns]] de la |
|
||||||
configuración, Yii intenta mostrar todas las columnas posibles del modelo del proveedor |
|
||||||
de datos. |
|
||||||
|
|
||||||
|
|
||||||
### Clases de columna <span id="column-classes"></span> |
|
||||||
|
|
||||||
Las columnas de la cuadrícula se pueden personalizar usando diferentes clases de columna: |
|
||||||
|
|
||||||
```php |
|
||||||
echo GridView::widget([ |
|
||||||
'dataProvider' => $dataProvider, |
|
||||||
'columns' => [ |
|
||||||
[ |
|
||||||
'class' => 'yii\grid\SerialColumn', // <-- aquí |
|
||||||
// puede configurar propiedades adicionales aquí |
|
||||||
], |
|
||||||
``` |
|
||||||
|
|
||||||
Además de las clases de columna proporcionadas por Yii que se revisarán más abajo, |
|
||||||
puede crear sus propias clases de columna. |
|
||||||
|
|
||||||
Cada clase de columna extiende [[yii\grid\Column]] de modo que hay algunas opciones |
|
||||||
comunes que puede establecer al configurar las columnas de una cuadrícula. |
|
||||||
|
|
||||||
- [[yii\grid\Column::header|header]] permite establecer el contenida para la fila cabecera |
|
||||||
- [[yii\grid\Column::footer|footer]] permite establece el contenido de la fila al pie |
|
||||||
- [[yii\grid\Column::visible|visible]] define si la columna debería ser visible. |
|
||||||
- [[yii\grid\Column::content|content]] le permite pasar una función PHP válida que devuelva datos para una fila. El formato es el siguiente: |
|
||||||
|
|
||||||
```php |
|
||||||
function ($model, $key, $index, $column) { |
|
||||||
return 'una cadena'; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Puede indicar varias opciones HTML del contenedor pasando _arrays_ a: |
|
||||||
|
|
||||||
- [[yii\grid\Column::headerOptions|headerOptions]] |
|
||||||
- [[yii\grid\Column::footerOptions|footerOptions]] |
|
||||||
- [[yii\grid\Column::filterOptions|filterOptions]] |
|
||||||
- [[yii\grid\Column::contentOptions|contentOptions]] |
|
||||||
|
|
@ -1,127 +0,0 @@ |
|||||||
Autenticación |
|
||||||
============= |
|
||||||
|
|
||||||
A diferencia de las aplicaciones Web, las API RESTful son usualmente sin estado (stateless), lo que permite que las sesiones o las cookies |
|
||||||
no sean usadas. Por lo tanto, cada petición debe llevar alguna suerte de credenciales de autenticación, |
|
||||||
porque la autenticación del usuario no puede ser mantenida por las sesiones o las cookies. Una práctica común |
|
||||||
es enviar una pieza (token) secreta de acceso con cada petición para autenticar al usuario. Dado que una pieza de autenticación |
|
||||||
puede ser usada para identificar y autenticar solamente a un usuario, **la API de peticiones tiene que ser siempre enviado |
|
||||||
vía HTTPS para prevenir ataques tipo "man-in-the-middle" (MitM) **. |
|
||||||
|
|
||||||
Hay muchas maneras de enviar una token (pieza) de acceso: |
|
||||||
|
|
||||||
* [Autenticación Básica HTTP](https://es.wikipedia.org/wiki/Autenticaci%C3%B3n_de_acceso_b%C3%A1sica): la pieza de acceso |
|
||||||
es enviada como nombre de usuario. Esto sólo debe de ser usado cuando la pieza de acceso puede ser guardada |
|
||||||
de forma segura en la parte del API del consumidor. Por ejemplo, el API del consumidor es un programa ejecutándose en un servidor. |
|
||||||
* Parámetro de la consulta: la pieza de acceso es enviada como un parámetro de la consulta en la URL de la API, p.e., |
|
||||||
`https://example.com/users?access-token=xxxxxxxx`. Debido que muchos servidores dejan los parámetros de consulta en los logs del servidor, |
|
||||||
esta aproximación suele ser usada principalmente para servir peticiones `JSONP` |
|
||||||
que no usen las cabeceras HTTP para enviar piezas de acceso. |
|
||||||
* [OAuth 2](http://oauth.net/2/): la pieza de acceso es obtenida por el consumidor por medio de una autorización del servidor |
|
||||||
y enviada al API del servidor según el protocolo |
|
||||||
OAuth 2 [tokens HTTP del portador](https://datatracker.ietf.org/doc/html/rfc6750). |
|
||||||
|
|
||||||
Yii soporta todos los métodos anteriores de autenticación. Puedes crear nuevos métodos de autenticación de una forma fácil. |
|
||||||
|
|
||||||
Para activar la autenticación para tus APIs, sigue los pasos siguientes: |
|
||||||
|
|
||||||
1. Configura el componente `user` de la aplicación: |
|
||||||
- Define la propiedad [[yii\web\User::enableSession|enableSession]] como `false`. |
|
||||||
- Define la propiedad [[yii\web\User::loginUrl|loginUrl]] como `null` para mostrar un error HTTP 403 en vez de redireccionar a la pantalla de login. |
|
||||||
2. Especifica cuál método de autenticación planeas usar configurando el comportamiento (behavior) `authenticator` en tus |
|
||||||
clases de controladores REST. |
|
||||||
3. Implementa [[yii\web\IdentityInterface::findIdentityByAccessToken()]] en tu [[yii\web\User::identityClass|clase de identidad de usuarios]]. |
|
||||||
|
|
||||||
El paso 1 no es necesario pero sí recomendable para las APIs RESTful, pues son sin estado (stateless). |
|
||||||
Cuando [[yii\web\User::enableSession|enableSession]] es `false`, el estado de autenticación del usuario puede NO persistir entre peticiones usando sesiones. |
|
||||||
Si embargo, la autenticación será realizada para cada petición, lo que se consigue en los pasos 2 y 3. |
|
||||||
|
|
||||||
> Tip:Puedes configurar [[yii\web\User::enableSession|enableSession]] del componente de la aplicación `user` en la configuración |
|
||||||
> de las aplicaciones si estás desarrollando APIs RESTful en términos de un aplicación. Si desarrollas un módulo de las APIs RESTful, |
|
||||||
> puedes poner la siguiente línea en el método del módulo `init()`, tal y como sigue: |
|
||||||
> |
|
||||||
> ```php |
|
||||||
> public function init() |
|
||||||
> { |
|
||||||
> parent::init(); |
|
||||||
> \Yii::$app->user->enableSession = false; |
|
||||||
> } |
|
||||||
> ``` |
|
||||||
|
|
||||||
Por ejemplo, para usar HTTP Basic Auth, puedes configurar el comportamiento (behavior) `authenticator` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::class, |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Si quieres implementar los tres métodos de autenticación explicados antes, puedes usar `CompositeAuth` de la siguiente manera, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\CompositeAuth; |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
use yii\filters\auth\HttpBearerAuth; |
|
||||||
use yii\filters\auth\QueryParamAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => CompositeAuth::class, |
|
||||||
'authMethods' => [ |
|
||||||
HttpBasicAuth::class, |
|
||||||
HttpBearerAuth::class, |
|
||||||
QueryParamAuth::class, |
|
||||||
], |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cada elemento en `authMethods` debe de ser el nombre de una clase de método de autenticación o un array de configuración. |
|
||||||
|
|
||||||
|
|
||||||
La implementación de `findIdentityByAccessToken()` es específico de la aplicación. Por ejemplo, en escenarios simples |
|
||||||
cuando cada usuario sólo puede tener un token de acceso, puedes almacenar este token en la columna `access_token` |
|
||||||
en la tabla de usuario. El método debe de ser inmediatamente implementado en la clase `User` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\IdentityInterface; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements IdentityInterface |
|
||||||
{ |
|
||||||
public static function findIdentityByAccessToken($token, $type = null) |
|
||||||
{ |
|
||||||
return static::findOne(['access_token' => $token]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Después que la autenticación es activada, tal y como se describe arriba, para cada petición de la API, el controlador solicitado |
|
||||||
puede intentar autenticar al usuario en su evento `beforeAction()`. |
|
||||||
|
|
||||||
Si la autenticación tiene éxito, el controlador realizará otras comprobaciones (como son límite del ratio, autorización) |
|
||||||
y entonces ejecutar la acción. La identidad del usuario autenticado puede ser recuperada via `Yii::$app->user->identity`. |
|
||||||
|
|
||||||
Si la autenticación falla, una respuesta con estado HTTP 401 será devuelta junto con otras cabeceras apropiadas |
|
||||||
(tal como la cabecera para autenticación básica HTTP `WWW-Authenticate`). |
|
||||||
|
|
||||||
|
|
||||||
## Autorización <span id="authorization"></span> |
|
||||||
|
|
||||||
Después de que un usuario se ha autenticado, probablementer querrás comprobar si él o ella tiene los permisos para realizar |
|
||||||
la acción solicitada. Este proceso es llamado *autorización (authorization)* y está cubierto en detalle |
|
||||||
en la [Sección de Autorización](security-authorization.md). |
|
||||||
|
|
||||||
Si tus controladores extienden de [[yii\rest\ActiveController]], puedes sobreescribir |
|
||||||
el método [[yii\rest\ActiveController::checkAccess()|checkAccess()]] para realizar la comprobación de la autorización. |
|
||||||
El método será llamado por las acciones contenidas en [[yii\rest\ActiveController]]. |
|
@ -1,156 +0,0 @@ |
|||||||
Controladores |
|
||||||
============= |
|
||||||
|
|
||||||
Después de crear las clases de recursos y especificar cómo debe ser el formato de datos de recursos, el siguiente paso |
|
||||||
es crear acciones del controlador para exponer los recursos a los usuarios finales a través de las APIs RESTful. |
|
||||||
|
|
||||||
Yii ofrece dos clases controlador base para simplificar tu trabajo de crear acciones REST: |
|
||||||
[[yii\rest\Controller]] y [[yii\rest\ActiveController]]. La diferencia entre estos dos controladores |
|
||||||
es que este último proporciona un conjunto predeterminado de acciones que están específicamente diseñado para trabajar con |
|
||||||
los recursos representados como [Active Record](db-active-record.md). Así que si estás utilizando [Active Record](db-active-record.md) |
|
||||||
y te sientes cómodo con las acciones integradas provistas, podrías considerar extender tus controladores |
|
||||||
de [[yii\rest\ActiveController]], lo que te permitirá crear potentes APIs RESTful con un mínimo de código. |
|
||||||
|
|
||||||
Ambos [[yii\rest\Controller]] y [[yii\rest\ActiveController]] proporcionan las siguientes características, |
|
||||||
algunas de las cuales se describen en detalle en las siguientes secciones: |
|
||||||
|
|
||||||
* Método de Validación HTTP; |
|
||||||
* [Negociación de contenido y formato de datos](rest-response-formatting.md); |
|
||||||
* [Autenticación](rest-authentication.md); |
|
||||||
* [Límite de Rango](rest-rate-limiting.md). |
|
||||||
|
|
||||||
[[yii\rest\ActiveController]] además provee de las siguientes características: |
|
||||||
|
|
||||||
* Un conjunto de acciones comunes necesarias: `index`, `view`, `create`, `update`, `delete`, `options`; |
|
||||||
* La autorización del usuario de acuerdo a la acción y recurso solicitado. |
|
||||||
|
|
||||||
|
|
||||||
## Creando Clases de Controlador <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Al crear una nueva clase de controlador, una convención para nombrar la clase del controlador es utilizar |
|
||||||
el nombre del tipo de recurso en singular. Por ejemplo, para servir información de usuario, |
|
||||||
el controlador puede ser nombrado como `UserController`. |
|
||||||
|
|
||||||
Crear una nueva acción es similar a crear una acción para una aplicación Web. La única diferencia |
|
||||||
es que en lugar de renderizar el resultado utilizando una vista llamando al método `render()`, para las acciones REST |
|
||||||
regresas directamente los datos. El [[yii\rest\Controller::serializer|serializer]] y el |
|
||||||
[[yii\web\Response|response object]] se encargarán de la conversión de los datos originales |
|
||||||
al formato solicitado. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
public function actionView($id) |
|
||||||
{ |
|
||||||
return User::findOne($id); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Filtros <span id="filters"></span> |
|
||||||
|
|
||||||
La mayoría de las características API REST son proporcionadas por [[yii\rest\Controller]] son implementadas en los términos de [filtros](structure-filters.md). |
|
||||||
En particular, los siguientes filtros se ejecutarán en el orden en que aparecen: |
|
||||||
|
|
||||||
* [[yii\filters\ContentNegotiator|contentNegotiator]]: soporta la negociación de contenido, que se explica en |
|
||||||
la sección [Formateo de respuestas](rest-response-formatting.md); |
|
||||||
* [[yii\filters\VerbFilter|verbFilter]]: soporta métodos de validación HTTP; |
|
||||||
* [[yii\filters\auth\AuthMethod|authenticator]]: soporta la autenticación de usuarios, que se explica en |
|
||||||
la sección [Autenticación](rest-authentication.md); |
|
||||||
* [[yii\filters\RateLimiter|rateLimiter]]: soporta la limitación de rango, que se explica en |
|
||||||
la sección [Límite de Rango](rest-rate-limiting.md). |
|
||||||
|
|
||||||
Estos filtros se declaran nombrándolos en el método [[yii\rest\Controller::behaviors()|behaviors()]]. |
|
||||||
Puede sobrescribir este método para configurar filtros individuales, desactivar algunos de ellos, o añadir los tuyos. |
|
||||||
Por ejemplo, si sólo deseas utilizar la autenticación básica HTTP, puede escribir el siguiente código: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::class, |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Extendiendo `ActiveController` <span id="extending-active-controller"></span> |
|
||||||
|
|
||||||
Si tu clase controlador extiende de [[yii\rest\ActiveController]], debe establecer |
|
||||||
su propiedad [[yii\rest\ActiveController::modelClass|modelClass]] con el nombre de la clase del recurso |
|
||||||
que planeas servir a través de este controlador. La clase debe extender de [[yii\db\ActiveRecord]]. |
|
||||||
|
|
||||||
|
|
||||||
### Personalizando Acciones <span id="customizing-actions"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\rest\ActiveController]] provee de las siguientes acciones: |
|
||||||
|
|
||||||
* [[yii\rest\IndexAction|index]]: listar recursos página por página; |
|
||||||
* [[yii\rest\ViewAction|view]]: devolver el detalle de un recurso específico; |
|
||||||
* [[yii\rest\CreateAction|create]]: crear un nuevo recurso; |
|
||||||
* [[yii\rest\UpdateAction|update]]: actualizar un recurso existente; |
|
||||||
* [[yii\rest\DeleteAction|delete]]: eliminar un recurso específico; |
|
||||||
* [[yii\rest\OptionsAction|options]]: devolver los métodos HTTP soportados. |
|
||||||
|
|
||||||
Todas esta acciones se declaran a través de método [[yii\rest\ActiveController::actions()|actions()]]. |
|
||||||
Puedes configurar estas acciones o desactivar alguna de ellas sobrescribiendo el método `actions()`, como se muestra a continuación, |
|
||||||
|
|
||||||
```php |
|
||||||
public function actions() |
|
||||||
{ |
|
||||||
$actions = parent::actions(); |
|
||||||
|
|
||||||
// disable the "delete" and "create" actions |
|
||||||
unset($actions['delete'], $actions['create']); |
|
||||||
|
|
||||||
// customize the data provider preparation with the "prepareDataProvider()" method |
|
||||||
$actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider']; |
|
||||||
|
|
||||||
return $actions; |
|
||||||
} |
|
||||||
|
|
||||||
public function prepareDataProvider() |
|
||||||
{ |
|
||||||
// prepare and return a data provider for the "index" action |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Por favor, consulta las referencias de clases de acciones individuales para aprender las opciones de configuración disponibles para cada una. |
|
||||||
|
|
||||||
|
|
||||||
### Realizando Comprobación de Acceso <span id="performing-access-check"></span> |
|
||||||
|
|
||||||
Al exponer los recursos a través de RESTful APIs, a menudo es necesario comprobar si el usuario actual tiene permiso |
|
||||||
para acceder y manipular el/los recurso solicitado/s. Con [[yii\rest\ActiveController]], esto puede lograrse |
|
||||||
sobrescribiendo el método [[yii\rest\ActiveController::checkAccess()|checkAccess()]] como a continuación, |
|
||||||
|
|
||||||
```php |
|
||||||
/** |
|
||||||
* Checks the privilege of the current user. |
|
||||||
* |
|
||||||
* This method should be overridden to check whether the current user has the privilege |
|
||||||
* to run the specified action against the specified data model. |
|
||||||
* If the user does not have access, a [[ForbiddenHttpException]] should be thrown. |
|
||||||
* |
|
||||||
* @param string $action the ID of the action to be executed |
|
||||||
* @param \yii\base\Model $model the model to be accessed. If `null`, it means no specific model is being accessed. |
|
||||||
* @param array $params additional parameters |
|
||||||
* @throws ForbiddenHttpException if the user does not have access |
|
||||||
*/ |
|
||||||
public function checkAccess($action, $model = null, $params = []) |
|
||||||
{ |
|
||||||
// check if the user can access $action and $model |
|
||||||
// throw ForbiddenHttpException if access should be denied |
|
||||||
if ($action === 'update' || $action === 'delete') { |
|
||||||
if ($model->author_id !== \Yii::$app->user->id) |
|
||||||
throw new \yii\web\ForbiddenHttpException(sprintf('You can only %s articles that you\'ve created.', $action)); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
El método `checkAccess()` será llamado por defecto en las acciones predeterminadas de [[yii\rest\ActiveController]]. Si creas |
|
||||||
nuevas acciones y también deseas llevar a cabo la comprobación de acceso, debe llamar a este método de forma explícita en las nuevas acciones. |
|
||||||
|
|
||||||
> Tip: Puedes implementar `checkAccess()` mediante el uso del [Componente Role-Based Access Control (RBAC)](security-authorization.md). |
|
@ -1,94 +0,0 @@ |
|||||||
Manejo de errores |
|
||||||
================= |
|
||||||
|
|
||||||
Cuando se maneja una petición de API RESTful, si ocurre un error en la petición del usuario o si algo inesperado |
|
||||||
ocurre en el servidor, simplemente puedes lanzar una excepción para notificar al usuario que algo erróneo ocurrió. |
|
||||||
Si puedes identificar la causa del error (p.e., el recurso solicitado no existe), debes considerar lanzar una excepción |
|
||||||
con el código HTTP de estado apropiado (p.e., [[yii\web\NotFoundHttpException]] representa un código de estado 404). |
|
||||||
Yii enviará la respuesta a continuación con el correspondiente código de estado HTTP y el texto. Yii puede incluir también |
|
||||||
la representación serializada de la excepción en el cuerpo de la respuesta. |
|
||||||
Por ejemplo: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 404 Not Found |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
La siguiente lista sumariza los códigos de estado HTTP que son usados por el framework REST: |
|
||||||
|
|
||||||
* `200`: OK. Todo ha funcionado como se esperaba. |
|
||||||
* `201`: El recurso ha creado con éxito en respuesta a la petición `POST`. La cabecera de situación `Location` |
|
||||||
contiene la URL apuntando al nuevo recurso creado. |
|
||||||
* `204`: La petición ha sido manejada con éxito y el cuerpo de la respuesta no tiene contenido (como una petición `DELETE`). |
|
||||||
* `304`: El recurso no ha sido modificado. Puede usar la versión en caché. |
|
||||||
* `400`: Petición errónea. Esto puede estar causado por varias acciones de el usuario, como proveer un JSON no válido |
|
||||||
en el cuerpo de la petición, proveyendo parámetros de acción no válidos, etc. |
|
||||||
* `401`: Autenticación fallida. |
|
||||||
* `403`: El usuario autenticado no tiene permitido acceder a la API final. |
|
||||||
* `404`: El recurso pedido no existe. |
|
||||||
* `405`: Método no permitido. Por favor comprueba la cabecera `Allow` por los métodos HTTP permitidos. |
|
||||||
* `415`: Tipo de medio no soportado. El tipo de contenido pedido o el número de versión no es válido. |
|
||||||
* `422`: La validación de datos ha fallado (en respuesta a una petición `POST` , por ejemplo). Por favor, comprueba en el cuerpo de la respuesta el mensaje detallado. |
|
||||||
* `429`: Demasiadas peticiones. La petición ha sido rechazada debido a un limitación de rango. |
|
||||||
* `500`: Error interno del servidor. Esto puede estar causado por errores internos del programa. |
|
||||||
|
|
||||||
|
|
||||||
## Personalizar la Respuesta al Error <span id="customizing-error-response"></span> |
|
||||||
|
|
||||||
A veces puedes querer personalizar el formato de la respuesta del error por defecto . Por ejemplo, en lugar de depender |
|
||||||
del uso de diferentes estados HTTP para indicar los diferentes errores, puedes querer usar siempre el estado HTTP 200 |
|
||||||
y encapsular el código de estado HTTP real como parte de una estructura JSON en la respuesta, como se muestra a continuación, |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"success": false, |
|
||||||
"data": { |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Para lograrlo, puedes responder al evento `beforeSend` del componente `response` en la configuración de la aplicación: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
// ... |
|
||||||
'components' => [ |
|
||||||
'response' => [ |
|
||||||
'class' => 'yii\web\Response', |
|
||||||
'on beforeSend' => function ($event) { |
|
||||||
$response = $event->sender; |
|
||||||
if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) { |
|
||||||
$response->data = [ |
|
||||||
'success' => $response->isSuccessful, |
|
||||||
'data' => $response->data, |
|
||||||
]; |
|
||||||
$response->statusCode = 200; |
|
||||||
} |
|
||||||
}, |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
El anterior código reformateará la respuesta (sea exitosa o fallida) como se explicó cuando |
|
||||||
`suppress_response_code` es pasado como un parámetro `GET`. |
|
@ -1,203 +0,0 @@ |
|||||||
Guía Breve |
|
||||||
========== |
|
||||||
|
|
||||||
Yii ofrece todo un conjunto de herramientas para simplificar la tarea de implementar un |
|
||||||
servicio web APIs RESTful. |
|
||||||
En particular, Yii soporta las siguientes características sobre APIs RESTful; |
|
||||||
|
|
||||||
* Prototipado rápido con soporte para APIs comunes para [Active Record](db-active-record.md); |
|
||||||
* Formato de respuesta de negocio (soporta JSON y XML por defecto); |
|
||||||
* Personalización de objetos serializados con soporte para campos de salida seleccionables; |
|
||||||
* Formateo apropiado de colecciones de datos y validación de errores; |
|
||||||
* Soporte para [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS); |
|
||||||
* Eficiente enrutamiento con una adecuada comprobación del verbo(verb) HTTP; |
|
||||||
* Incorporado soporte para las `OPTIONS` y `HEAD` verbos; |
|
||||||
* Autenticación y autorización; |
|
||||||
* Cacheo de datos y cacheo HTTP; |
|
||||||
* Limitación de rango; |
|
||||||
|
|
||||||
|
|
||||||
A continuación, utilizamos un ejemplo para ilustrar como se puede construir un conjunto de APIs RESTful con un esfuerzo mínimo de codificación. |
|
||||||
|
|
||||||
Supongamos que deseas exponer los datos de los usuarios vía APIs RESTful. Los datos de usuario son almacenados en la tabla DB `user`, |
|
||||||
y ya tienes creado la clase [[yii\db\ActiveRecord|ActiveRecord]] `app\models\User` para acceder a los datos del usuario. |
|
||||||
|
|
||||||
|
|
||||||
## Creando un controlador <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Primero, crea una clase controladora `app\controllers\UserController` como la siguiente, |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
La clase controladora extiende de [[yii\rest\ActiveController]]. Especificado por [[yii\rest\ActiveController::modelClass|modelClass]] |
|
||||||
como `app\models\User`, el controlador sabe que modelo puede ser usado para recoger y manipular sus datos. |
|
||||||
|
|
||||||
|
|
||||||
## Configurando las reglas de las URL <span id="configuring-url-rules"></span> |
|
||||||
|
|
||||||
A continuación, modifica la configuración del componente `urlManager` en la configuración de tu aplicación: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
La configuración anterior principalmente añade una regla URL para el controlador `user` de manera |
|
||||||
que los datos de user pueden ser accedidos y manipulados con URLs amigables y verbos HTTP significativos. |
|
||||||
|
|
||||||
|
|
||||||
## Habilitando entradas JSON <span id="enabling-json-input"></span> |
|
||||||
|
|
||||||
Para permitir que la API acepte datos de entrada con formato JSON, configura la propiedad [[yii\web\Request::$parsers|parsers]] |
|
||||||
del componente de aplicación `request` para usar [[yii\web\JsonParser]] para entradas JSON: |
|
||||||
|
|
||||||
```php |
|
||||||
'request' => [ |
|
||||||
'parsers' => [ |
|
||||||
'application/json' => 'yii\web\JsonParser', |
|
||||||
] |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
> Tip: La configuración anterior es opcional. Sin la configuración anterior, la API sólo reconocería |
|
||||||
`application/x-www-form-urlencoded` y `multipart/form-data` como formatos de entrada. |
|
||||||
|
|
||||||
|
|
||||||
## Probándolo <span id="trying-it-out"></span> |
|
||||||
|
|
||||||
Con la mínima cantidad de esfuerzo, tienes ya finalizado tu tarea de crear las APIs RESTful |
|
||||||
para acceder a los datos de user. Las APIs que tienes creado incluyen: |
|
||||||
|
|
||||||
* `GET /users`: una lista de todos los usuarios página por página; |
|
||||||
* `HEAD /users`: muestra la información general de la lista de usuarios; |
|
||||||
* `POST /users`: crea un nuevo usuario; |
|
||||||
* `GET /users/123`: devuelve los detalles del usuario 123; |
|
||||||
* `HEAD /users/123`: muestra la información general del usuario 123; |
|
||||||
* `PATCH /users/123` y `PUT /users/123`: actualiza el usuario 123; |
|
||||||
* `DELETE /users/123`: elimina el usuario 123; |
|
||||||
* `OPTIONS /users`: muestra los verbos compatibles respecto al punto final `/users`; |
|
||||||
* `OPTIONS /users/123`: muestra los verbos compatibles respecto al punto final `/users/123`. |
|
||||||
|
|
||||||
> Info: Yii automáticamente pluraliza los nombres de los controladores para usarlo en los puntos finales. |
|
||||||
> Puedes configurar esto usando la propiedad [[yii\rest\UrlRule::$pluralize]]. |
|
||||||
|
|
||||||
Puedes acceder a tus APIs con el comando `curl` de la siguiente manera, |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Intenta cambiar el tipo de contenido aceptado para ser `application/xml`, y verá que el resultado |
|
||||||
se devuelve en formato XML: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/xml" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/xml |
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<response> |
|
||||||
<item> |
|
||||||
<id>1</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
<item> |
|
||||||
<id>2</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
... |
|
||||||
</response> |
|
||||||
``` |
|
||||||
|
|
||||||
El siguiente comando creará un nuevo usuario mediante el envío de una petición POST con los datos del usuario en formato JSON: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://localhost/users" -d '{"username": "example", "email": "user@example.com"}' |
|
||||||
|
|
||||||
HTTP/1.1 201 Created |
|
||||||
... |
|
||||||
Location: http://localhost/users/1 |
|
||||||
Content-Length: 99 |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{"id":1,"username":"example","email":"user@example.com","created_at":1414674789,"updated_at":1414674789} |
|
||||||
``` |
|
||||||
|
|
||||||
> Tip: También puedes acceder a tus APIs a través del navegador web introduciendo la URL `http://localhost/users`. |
|
||||||
Sin embargo, es posible que necesites algunos plugins para el navegador para enviar cabeceras especificas en la petición. |
|
||||||
|
|
||||||
Como se puede ver, en las cabeceras de la respuesta, hay información sobre la cuenta total, número de páginas, etc. |
|
||||||
También hay enlaces que permiten navegar por otras páginas de datos. Por ejemplo, `http://localhost/users?page=2` |
|
||||||
le daría la página siguiente de los datos de usuario. |
|
||||||
|
|
||||||
Utilizando los parámetros `fields` y `expand`, puedes también especificar que campos deberían ser incluidos en el resultado. |
|
||||||
Por ejemplo, la URL `http://localhost/users?fields=id,email` sólo devolverá los campos `id` y `email`. |
|
||||||
|
|
||||||
|
|
||||||
> Info: Puedes haber notado que el resultado de `http://localhost/users` incluye algunos campos sensibles, |
|
||||||
> tal como `password_hash`, `auth_key`. Seguramente no quieras que éstos aparecieran en el resultado de tu API. |
|
||||||
> Puedes y deberías filtrar estos campos como se describe en la sección [Response Formatting](rest-response-formatting.md). |
|
||||||
|
|
||||||
|
|
||||||
## Resumen <span id="summary"></span> |
|
||||||
|
|
||||||
Utilizando el framework Yii API RESTful, implementa un punto final API en términos de una acción de un controlador, y utiliza |
|
||||||
un controlador para organizar las acciones que implementan los puntos finales para un sólo tipo de recurso. |
|
||||||
|
|
||||||
Los recursos son representados como modelos de datos que extienden de la clase [[yii\base\Model]]. |
|
||||||
Si estás trabajando con bases de datos (relacionales o NoSQL), es recomendable utilizar [[yii\db\ActiveRecord|ActiveRecord]] |
|
||||||
para representar los recursos. |
|
||||||
|
|
||||||
Puedes utilizar [[yii\rest\UrlRule]] para simplificar el enrutamiento de los puntos finales de tu API. |
|
||||||
|
|
||||||
Aunque no es obligatorio, es recomendable que desarrolles tus APIs RESTful como una aplicación separada, diferente de |
|
||||||
tu WEB front end y tu back end para facilitar el mantenimiento. |
|
@ -1,44 +0,0 @@ |
|||||||
Limitando el rango (rate) |
|
||||||
========================= |
|
||||||
|
|
||||||
Para prevenir el abuso, puedes considerar añadir un *límitación del rango (rate limiting)* para tus APIs. Por ejemplo, |
|
||||||
puedes querer limitar el uso del API de cada usuario a un máximo de 100 llamadas al API dentro de un periodo de 10 minutos. |
|
||||||
Si se reciben demasiadas peticiones de un usuario dentro del periodo de tiempo declarado, deveríá devolverse una respuesta con código de estado 429 (que significa "Demasiadas peticiones"). |
|
||||||
|
|
||||||
Para activar la limitación de rango, la clase [[yii\web\User::identityClass|user identity class]] debe implementar [[yii\filters\RateLimitInterface]]. |
|
||||||
Este interface requiere la implementación de tres métodos: |
|
||||||
|
|
||||||
* `getRateLimit()`: devuelve el número máximo de peticiones permitidas y el periodo de tiempo (p.e., `[100, 600]` significa que como mucho puede haber 100 llamadas al API dentro de 600 segundos). |
|
||||||
* `loadAllowance()`: devuelve el número de peticiones que quedan permitidas y el tiempo (fecha/hora) UNIX |
|
||||||
con el último límite del rango que ha sido comprobado. |
|
||||||
* `saveAllowance()`: guarda ambos, el número restante de peticiones permitidas y el tiempo actual (fecha/hora) UNIX . |
|
||||||
|
|
||||||
Puedes usar dos columnas en la tabla de usuario para guardar la información de lo permitido y la fecha/hora (timestamp). Con ambas definidas, |
|
||||||
entonces `loadAllowance()` y `saveAllowance()` pueden ser utilizados para leer y guardar los valores de las dos columnas correspondientes al actual usuario autenticado. |
|
||||||
Para mejorar el desempeño, también puedes considerar almacenar esas piezas de información en caché o almacenamiento NoSQL. |
|
||||||
|
|
||||||
Una vez que la clase de identidad implementa la interfaz requerida, Yii utilizará automáticamente [[yii\filters\RateLimiter]] |
|
||||||
configurado como un filtro de acción para que [[yii\rest\Controller]] compruebe el límite de rango. El limitador de rango |
|
||||||
lanzará una excepeción [[yii\web\TooManyRequestsHttpException]] cuando el límite del rango sea excedido. |
|
||||||
|
|
||||||
Puedes configurar el limitador de rango |
|
||||||
en tu clase controlador REST como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['rateLimiter']['enableRateLimitHeaders'] = false; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando se activa el límite de rango, por defecto todas las respuestas serán enviadas con la siguiente cabecera HTTP conteniendo |
|
||||||
información sobre el límite actual de rango: |
|
||||||
|
|
||||||
* `X-Rate-Limit-Limit`, el máximo número de peticiones permitidas en un periodo de tiempo |
|
||||||
* `X-Rate-Limit-Remaining`, el número de peticiones restantes en el periodo de tiempo actual |
|
||||||
* `X-Rate-Limit-Reset`, el número de segundos a esperar para pedir el máximo número de peticiones permitidas |
|
||||||
|
|
||||||
Puedes desactivar estas cabeceras configurando [[yii\filters\RateLimiter::enableRateLimitHeaders]] a `false`, |
|
||||||
tal y como en el anterior ejemplo. |
|
@ -1,190 +0,0 @@ |
|||||||
Recursos |
|
||||||
========= |
|
||||||
|
|
||||||
Las APIs RESTful lo son todos para acceder y manipular *recursos (resources)*. Puedes observar los recursos en el paradigma MVC en [Modelos (models)](structure-models.md) . |
|
||||||
|
|
||||||
Mientras que no hay restricción a cómo representar un recurso, en YII usualmente, puedes representar recursos como objetos de la clase [[yii\base\Model]] o sus clases hijas (p.e. [[yii\db\ActiveRecord]]), por las siguientes razones: |
|
||||||
|
|
||||||
* [[yii\base\Model]] implementa el interface [[yii\base\Arrayable]] , el cual te permite personalizar como exponer los datos de los recursos a travès de las APIs RESTful. |
|
||||||
* [[yii\base\Model]] soporta [Validación de entrada (input validation)](input-validation.md), lo cual es muy usado en las APIs RESTful para soportar la entrada de datos. |
|
||||||
* [[yii\db\ActiveRecord]] provee un poderoso soporte para el acceso a datos en bases de datos y su manipulación, lo que lo le hace servir perfectamente si sus recursos de datos están en bases de datos. |
|
||||||
|
|
||||||
En esta sección, vamos principalmente a describir como la clase con recursos que extiende de [[yii\base\Model]] (o sus clases hijas) puede especificar qué datos puede ser devueltos vía las APIs RESTful. Si la clase de los recursos no extiende de [[yii\base\Model]], entonces todas las variables públicas miembro serán devueltas. |
|
||||||
|
|
||||||
|
|
||||||
## Campos (fields) <span id="fields"></span> |
|
||||||
|
|
||||||
Cuando incluimos un recurso en una respuesta de la API RESTful, el recurso necesita ser serializado en una cadena. |
|
||||||
Yii divide este proceso en dos pasos. Primero, el recurso es convertido en un array por [[yii\rest\Serializer]]. |
|
||||||
Segundo, el array es serializado en una cadena en el formato requerido (p.e. JSON, XML) por [[yii\web\ResponseFormatterInterface|response formatters]]. El primer paso es en el que debes de concentrarte principalmente cuando desarrolles una clase de un recurso. |
|
||||||
|
|
||||||
Sobreescribiendo [[yii\base\Model::fields()|fields()]] y/o [[yii\base\Model::extraFields()|extraFields()]], |
|
||||||
puedes especificar qué datos, llamados *fields*, en el recursos, pueden ser colocados en el array que le representa. |
|
||||||
La diferencia entre estos dos métodos es que el primero especifica el conjunto de campos por defecto que deben ser incluidos en el array que los representa, mientras que el último especifica campos adicionales que deben de ser incluidos en el array si una petición del usuario final para ellos vía el parámetro de consulta `expand`. Por ejemplo, |
|
||||||
|
|
||||||
``` |
|
||||||
// devuelve todos los campos declarados en fields() |
|
||||||
http://localhost/users |
|
||||||
|
|
||||||
// sólo devuelve los campos id y email, provistos por su declaración en fields() |
|
||||||
http://localhost/users?fields=id,email |
|
||||||
|
|
||||||
// devuelve todos los campos en fields() y el campo profile siempre y cuando esté declarado en extraFields() |
|
||||||
http://localhost/users?expand=profile |
|
||||||
|
|
||||||
// sólo devuelve los campos id, email y profile, siempre y cuando ellos estén declarados en fields() y extraFields() |
|
||||||
http://localhost/users?fields=id,email&expand=profile |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
### Sobreescribiendo `fields()` <span id="overriding-fields"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\base\Model::fields()]] devuelve todos los atributos de los modelos como si fueran campos, mientras [[yii\db\ActiveRecord::fields()]] sólo devuelve los atributos que tengan datos en la base de datos. |
|
||||||
|
|
||||||
Puedes sobreescribir `fields()` para añadir, quitar, renombrar o redefinir campos. El valor de retorno de `fields()` ha de estar en un array. Las claves del array son los nombres de los campos y los valores del array son las correspondientes definiciones de los campos que pueden ser tanto nombres de propiedades/atributos o funciones anónimas que devuelven los correspondientes valores del los campos. En el caso especial de que el nombre de un campo sea el mismo que su definición puedes omitir la clave en el array. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
// explícitamente lista cada campo, siendo mejor usarlo cuando quieras asegurarte que los cambios |
|
||||||
// en una tabla de la base de datos o en un atributo del modelo no provoque el cambio de tu campo (para mantener la compatibilidad anterior). |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
// el nombre de campo es el mismo nombre del atributo |
|
||||||
'id', |
|
||||||
// el nombre del campo es "email", su atributo se denomina "email_address" |
|
||||||
'email' => 'email_address', |
|
||||||
// el nombre del campo es "name", su valor es definido está definido por una función anónima de retrollamada (callback) |
|
||||||
'name' => function () { |
|
||||||
return $this->first_name . ' ' . $this->last_name; |
|
||||||
}, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// el ignorar algunos campos, es mejor usarlo cuando heredas de una implementación padre |
|
||||||
// y pones en la lista negra (blacklist) algunos campos sensibles |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
$fields = parent::fields(); |
|
||||||
|
|
||||||
// quita los campos con información sensible |
|
||||||
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); |
|
||||||
|
|
||||||
return $fields; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
> Warning: Dado que, por defecto, todos los atributos de un modelo pueden ser incluidos en la devolución del API, debes |
|
||||||
> examinar tus datos para estar seguro de que no contiene información sensible. Si se da este tipo de información, |
|
||||||
> debes sobreescribir `fields()` para filtrarlos. En el ejemplo anterior, escogemos |
|
||||||
> quitar `auth_key`, `password_hash` y `password_reset_token`. |
|
||||||
|
|
||||||
|
|
||||||
### Sobreescribiendo `extraFields()` <span id="overriding-extra-fields"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\base\Model::extraFields()]] no devuelve nada, mientras que [[yii\db\ActiveRecord::extraFields()]] devuelve los nombres de las relaciones que tienen datos (populated) obtenidos de la base de datos. |
|
||||||
|
|
||||||
El formato de devolución de los datos de `extraFields()` es el mismo que el de `fields()`. Usualmente, `extraFields()` es principalmente usado para especificar campos cuyos valores sean objetos. Por ejemplo, dado la siguiente declaración de campo, |
|
||||||
|
|
||||||
```php |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return ['id', 'email']; |
|
||||||
} |
|
||||||
|
|
||||||
public function extraFields() |
|
||||||
{ |
|
||||||
return ['profile']; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
la petición `http://localhost/users?fields=id,email&expand=profile` puede devolver los siguientes datos en formato JSON : |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "100@example.com", |
|
||||||
"profile": { |
|
||||||
"id": 100, |
|
||||||
"age": 30, |
|
||||||
} |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Enlaces (Links) <span id="links"></span> |
|
||||||
|
|
||||||
[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS), es una abreviación de Hipermedia es el Motor del Estado de la Aplicación (Hypermedia as the Engine of Application State), promueve que las APIs RESTfull devuelvan información que permita a los clientes descubrir las acciones que soportan los recursos devueltos. El sentido de HATEOAS es devolver un conjunto de hiperenlaces con relación a la información, cuando los datos de los recursos son servidos por las APIs. |
|
||||||
|
|
||||||
Las clases con recursos pueden soportar HATEOAS implementando el interfaz [[yii\web\Linkable]] . El interfaz contiene sólo un método [[yii\web\Linkable::getLinks()|getLinks()]] el cual debe de de devolver una lista de [[yii\web\Link|links]]. |
|
||||||
Típicamente, debes devolver al menos un enlace `self` representando la URL al mismo recurso objeto. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\Link; |
|
||||||
use yii\web\Linkable; |
|
||||||
use yii\helpers\Url; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements Linkable |
|
||||||
{ |
|
||||||
public function getLinks() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true), |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando un objeto `User` es devuelto en una respuesta, puede contener un elemento `_links` representando los enlaces relacionados con el usuario, por ejemplo, |
|
||||||
|
|
||||||
``` |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "user@example.com", |
|
||||||
// ... |
|
||||||
"_links" => { |
|
||||||
"self": { |
|
||||||
"href": "https://example.com/users/100" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Colecciones <span id="collections"></span> |
|
||||||
|
|
||||||
Los objetos de los recursos pueden ser agrupados en *collections*. Cada colección contiene una lista de recursos objeto del mismo tipo. |
|
||||||
|
|
||||||
Las colecciones pueden ser representadas como arrays pero, es usualmente más deseable representarlas como [proveedores de datos (data providers)](output-data-providers.md). Esto es así porque los proveedores de datos soportan paginación y ordenación de los recursos, lo cual es comunmente necesario en las colecciones devueltas con las APIs RESTful. Por ejemplo, la siguiente acción devuelve un proveedor de datos sobre los recursos post: |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\Controller; |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
use app\models\Post; |
|
||||||
|
|
||||||
class PostController extends Controller |
|
||||||
{ |
|
||||||
public function actionIndex() |
|
||||||
{ |
|
||||||
return new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando un proveedor de datos está enviando una respuesta con el API RESTful, [[yii\rest\Serializer]] llevará la actual página de los recursos y los serializa como un array de recursos objeto. Adicionalmente, [[yii\rest\Serializer]] |
|
||||||
puede incluir también la información de paginación a través de las cabeceras HTTP siguientes: |
|
||||||
|
|
||||||
* `X-Pagination-Total-Count`: Número total de recursos; |
|
||||||
* `X-Pagination-Page-Count`: Número de páginas; |
|
||||||
* `X-Pagination-Current-Page`: Página actual (iniciando en 1); |
|
||||||
* `X-Pagination-Per-Page`: Número de recursos por página; |
|
||||||
* `Link`: Un conjunto de enlaces de navegación permitiendo al cliente recorrer los recursos página a página. |
|
||||||
|
|
||||||
Un ejemplo se puede ver en la sección [Inicio rápido (Quick Start)](rest-quick-start.md#trying-it-out). |
|
@ -1,157 +0,0 @@ |
|||||||
Formato de Respuesta |
|
||||||
==================== |
|
||||||
|
|
||||||
Cuando se maneja una petición al API RESTful, una aplicación realiza usualmente los siguientes pasos que están relacionados |
|
||||||
con el formato de la respuesta: |
|
||||||
|
|
||||||
1. Determinar varios factores que pueden afectar al formato de la respuesta, como son el tipo de medio, lenguaje, versión, etc. |
|
||||||
Este proceso es también conocido como [negociación de contenido (content negotiation)](https://en.wikipedia.org/wiki/Content_negotiation). |
|
||||||
2. La conversión de objetos recurso en arrays, como está descrito en la sección [Recursos (Resources)](rest-resources.md). |
|
||||||
Esto es realizado por la clase [[yii\rest\Serializer]]. |
|
||||||
3. La conversión de arrays en cadenas con el formato determinado por el paso de negociación de contenido. Esto es |
|
||||||
realizado por los [[yii\web\ResponseFormatterInterface|formatos de respuesta]] registrados |
|
||||||
con la propiedad [[yii\web\Response::formatters|formatters]] del |
|
||||||
[componente de la aplicación](structure-application-components.md) `response`. |
|
||||||
|
|
||||||
|
|
||||||
## Negociación de contenido (Content Negotiation) <span id="content-negotiation"></span> |
|
||||||
|
|
||||||
Yii soporta la negociación de contenido a través del filtro [[yii\filters\ContentNegotiator]]. La clase de controlador base |
|
||||||
del API RESTful [[yii\rest\Controller]] está equipada con este filtro bajo el nombre `contentNegotiator`. |
|
||||||
El filtro provee tanto un formato de respuesta de negociación como una negociación de lenguaje. Por ejemplo, si la petición API RESTful |
|
||||||
contiene la siguiente cabecera, |
|
||||||
|
|
||||||
``` |
|
||||||
Accept: application/json; q=1.0, */*; q=0.1 |
|
||||||
``` |
|
||||||
|
|
||||||
puede obtener una respuesta en formato JSON, como a continuación: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Detrás de escena, antes de que sea ejecutada una acción del controlador del API RESTful, el filtro [[yii\filters\ContentNegotiator]] |
|
||||||
comprobará la cabecera HTTP `Accept` de la petición y definirá el [[yii\web\Response::format|response format]] |
|
||||||
como `'json'`. Después de que la acción sea ejecutada y devuelva el objeto recurso o la colección resultante, |
|
||||||
[[yii\rest\Serializer]] convertirá el resultado en un array. Y finalmente, [[yii\web\JsonResponseFormatter]] |
|
||||||
serializará el array en una cadena JSON incluyéndola en el cuerpo de la respuesta. |
|
||||||
|
|
||||||
Por defecto, el API RESTful soporta tanto el formato JSON como el XML. Para soportar un nuevo formato, debes configurar |
|
||||||
la propiedad [[yii\filters\ContentNegotiator::formats|formats]] del filtro `contentNegotiator` tal y como sigue, |
|
||||||
en las clases del controlador del API: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\web\Response; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Las claves de la propiedad `formats` son los tipos MIME soportados, mientras que los valores son los nombres de formato de respuesta correspondientes, |
|
||||||
los cuales deben ser soportados en [[yii\web\Response::formatters]]. |
|
||||||
|
|
||||||
|
|
||||||
## Serialización de Datos <span id="data-serializing"></span> |
|
||||||
|
|
||||||
Como hemos descrito antes, [[yii\rest\Serializer]] es la pieza central responsable de convertir |
|
||||||
objetos recurso o colecciones en arrays. Reconoce objetos tanto implementando [[yii\base\ArrayableInterface]] |
|
||||||
como [[yii\data\DataProviderInterface]]. El primer formateador es implementado principalmente para objetos recursos, |
|
||||||
mientras que el segundo para recursos collección. |
|
||||||
|
|
||||||
Puedes configurar el serializador definiendo la propiedad [[yii\rest\Controller::serializer]] con un array de configuración. |
|
||||||
Por ejemplo, a veces puedes querer ayudar a simplificar el trabajo de desarrollo del cliente incluyendo información de la paginación |
|
||||||
directamente en el cuerpo de la respuesta. Para hacer esto, configura la propiedad [[yii\rest\Serializer::collectionEnvelope]] |
|
||||||
como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
public $serializer = [ |
|
||||||
'class' => 'yii\rest\Serializer', |
|
||||||
'collectionEnvelope' => 'items', |
|
||||||
]; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Puedes obtener la respuesta que sigue para la petición `http://localhost/users`: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"items": [ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
], |
|
||||||
"_links": { |
|
||||||
"self": { |
|
||||||
"href": "http://localhost/users?page=1" |
|
||||||
}, |
|
||||||
"next": { |
|
||||||
"href": "http://localhost/users?page=2" |
|
||||||
}, |
|
||||||
"last": { |
|
||||||
"href": "http://localhost/users?page=50" |
|
||||||
} |
|
||||||
}, |
|
||||||
"_meta": { |
|
||||||
"totalCount": 1000, |
|
||||||
"pageCount": 50, |
|
||||||
"currentPage": 1, |
|
||||||
"perPage": 20 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
@ -1,92 +0,0 @@ |
|||||||
Enrutamiento |
|
||||||
============ |
|
||||||
|
|
||||||
Con las clases de controlador y recurso preparadas, puedes acceder a los recursos usando una URL como |
|
||||||
`http://localhost/index.php?r=user/create`, parecida a la que usas con aplicaciones Web normales. |
|
||||||
|
|
||||||
En la práctica, querrás usualmente usar URLs limpias y obtener ventajas de los verbos HTTP. |
|
||||||
Por ejemplo, una petición `POST /users` significaría acceder a la acción `user/create`. |
|
||||||
Esto puede realizarse fácilmente configurando el componente de la aplicación `urlManager` |
|
||||||
como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
En comparación con la gestión de URL en las aplicaciones Web, lo principalmente nuevo de lo anterior es el uso de |
|
||||||
[[yii\rest\UrlRule]] para el enrutamiento de las peticiones con el API RESTful. Esta clase especial de regla URL creará |
|
||||||
un conjunto completo de reglas URL hijas para soportar el enrutamiento y creación de URL para el/los controlador/es especificados. |
|
||||||
Por ejemplo, el código anterior es equivalente a las siguientes reglas: |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'PUT,PATCH users/<id>' => 'user/update', |
|
||||||
'DELETE users/<id>' => 'user/delete', |
|
||||||
'GET,HEAD users/<id>' => 'user/view', |
|
||||||
'POST users' => 'user/create', |
|
||||||
'GET,HEAD users' => 'user/index', |
|
||||||
'users/<id>' => 'user/options', |
|
||||||
'users' => 'user/options', |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Y los siguientes puntos finales del API son mantenidos por esta regla: |
|
||||||
|
|
||||||
* `GET /users`: lista de todos los usuarios página a página; |
|
||||||
* `HEAD /users`: muestra ĺa información resumen del usuario listado; |
|
||||||
* `POST /users`: crea un nuevo usuario; |
|
||||||
* `GET /users/123`: devuelve los detalles del usuario 123; |
|
||||||
* `HEAD /users/123`: muestra la información resumen del usuario 123; |
|
||||||
* `PATCH /users/123` y `PUT /users/123`: actualizan al usuario 123; |
|
||||||
* `DELETE /users/123`: borra el usuario 123; |
|
||||||
* `OPTIONS /users`: muestra los verbos soportados de acuerdo al punto final `/users`; |
|
||||||
* `OPTIONS /users/123`: muestra los verbos soportados de acuerdo al punto final `/users/123`. |
|
||||||
|
|
||||||
Puedes configurar las opciones `only` y `except` para explícitamente listar cuáles acciones soportar o cuáles |
|
||||||
deshabilitar, respectivamente. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'except' => ['delete', 'create', 'update'], |
|
||||||
], |
|
||||||
``` |
|
||||||
|
|
||||||
También puedes configurar las propiedades `patterns` o `extraPatterns` para redefinir patrones existentes o añadir nuevos soportados por esta regla. |
|
||||||
Por ejemplo, para soportar una nueva acción `search` para el punto final `GET /users/search`, configura la opción `extraPatterns` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'extraPatterns' => [ |
|
||||||
'GET search' => 'search', |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Puedes haber notado que el ID del controlador `user` aparece en formato plural `users` en los puntos finales de las URLs. |
|
||||||
Esto se debe a que [[yii\rest\UrlRule]] automáticamente pluraliza los IDs de los controladores al crear reglas URL hijas. |
|
||||||
Puedes desactivar este comportamiento definiendo la propiedad [[yii\rest\UrlRule::pluralize]] como `false`. |
|
||||||
|
|
||||||
> Info: La pluralización de los IDs de los controladores es realizada por [[yii\helpers\Inflector::pluralize()]]. Este método respeta |
|
||||||
reglas especiales de pluralización. Por ejemplo, la palabra `box` (caja) será pluralizada como `boxes` en vez de `boxs`. |
|
||||||
|
|
||||||
En caso de que la pluralización automática no encaje en tus requerimientos, puedes además configurar la propiedad |
|
||||||
[[yii\rest\UrlRule::controller]] para especificar explícitamente cómo mapear un nombre utilizado en un punto final URL |
|
||||||
a un ID de controlador. Por ejemplo, el siguiente código mapea el nombre `u` al ID del controlador `user`. |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => ['u' => 'user'], |
|
||||||
] |
|
||||||
``` |
|
@ -1,111 +0,0 @@ |
|||||||
Versionado |
|
||||||
========== |
|
||||||
|
|
||||||
Una buena API ha de ser *versionada*: los cambios y las nuevas características son implementadas en las nuevas versiones del API, en vez de estar continuamente modificando sólo una versión. Al contrario que en las aplicaciones Web, en las cuales tienes total control del código de ambas partes lado del cliente y lado del servidor, |
|
||||||
las APIs están destinadas a ser usadas por los clientes fuera de tu control. Por esta razón, compatibilidad hacia atrás (BC Backward compatibility) |
|
||||||
de las APIs ha de ser mantenida siempre que sea posible. Si es necesario un cambio que puede romper la BC, debes de introducirla en la nueva versión del API, e incrementar el número de versión. Los clientes que la usan pueden continuar usando la antigua versión de trabajo del API; los nuevos y actualizados clientes pueden obtener la nueva funcionalidad de la nueva versión del API. |
|
||||||
|
|
||||||
> Tip: referirse a [Semántica del versionado](https://semver.org/) |
|
||||||
para más información en el diseño del número de versión del API. |
|
||||||
|
|
||||||
Una manera común de implementar el versionado de la API es embeber el número de versión en las URLs de la API. |
|
||||||
Por ejemplo, `http://example.com/v1/users` se refiere al punto final `/users` de la versión 1 de la API. |
|
||||||
|
|
||||||
Otro método de versionado de la API, |
|
||||||
la cual está ganando predominancia recientemente, es poner el número de versión en las cabeceras de la petición HTTP. Esto se suele hacer típicamente a través la cabecera `Accept`: |
|
||||||
|
|
||||||
``` |
|
||||||
// vía parámetros |
|
||||||
Accept: application/json; version=v1 |
|
||||||
// vía de el tipo de contenido del proveedor |
|
||||||
Accept: application/vnd.company.myapp-v1+json |
|
||||||
``` |
|
||||||
|
|
||||||
Ambos métodos tienen sus pros y sus contras, y hay gran cantidad de debates sobre cada uno. Debajo puedes ver una estrategia |
|
||||||
práctica para el versionado de la API que es una mezcla de estos dos métodos: |
|
||||||
|
|
||||||
* Pon cada versión superior de la implementación de la API en un módulo separado cuyo ID es el número de la versión mayor (p.e. `v1`, `v2`). |
|
||||||
Naturalmente, las URLs de la API contendrán números de versión mayores. |
|
||||||
* Dentro de cada versión mayor (y por lo tanto, dentro del correspondiente módulo), usa la cabecera de HTTP `Accept` |
|
||||||
para determinar el número de la versión menor y escribe código condicional para responder a la menor versión como corresponde. |
|
||||||
|
|
||||||
Para cada módulo sirviendo una versión mayor, el módulo debe incluir las clases de recursos y y controladores |
|
||||||
que especifican la versión. Para separar mejor la responsabilidad del código, puedes conservar un conjunto común de |
|
||||||
clases base de recursos y controladores, y hacer subclases de ellas en cada versión individual del módulo. Dentro de las subclases, |
|
||||||
impementa el código concreto como por ejemplo `Model::fields()`. |
|
||||||
|
|
||||||
Tu código puede estar organizado como lo que sigue: |
|
||||||
|
|
||||||
``` |
|
||||||
api/ |
|
||||||
common/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
modules/ |
|
||||||
v1/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
v2/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
``` |
|
||||||
|
|
||||||
La configuración de tu aplicación puede tener este aspecto: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
'modules' => [ |
|
||||||
'v1' => [ |
|
||||||
'basePath' => '@app/modules/v1', |
|
||||||
'controllerNamespace' => 'app\modules\v1\controllers', |
|
||||||
], |
|
||||||
'v2' => [ |
|
||||||
'basePath' => '@app/modules/v2', |
|
||||||
'controllerNamespace' => 'app\modules\v2\controllers', |
|
||||||
], |
|
||||||
], |
|
||||||
'components' => [ |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']], |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']], |
|
||||||
], |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
Como consecuencia del código anterior, `http://example.com/v1/users` devolverá la lista de usuarios en la versión 1, mientras |
|
||||||
`http://example.com/v2/users` devolverá la versión 2 de los usuarios. |
|
||||||
|
|
||||||
Gracias a los módulos, el código de las diferentes principales versiones puede ser aislado. Pero los módulos hacen posible |
|
||||||
reutilizar el código a través de los módulos vía clases base comunes y otros recursos compartidos. |
|
||||||
|
|
||||||
Para tratar con versiones menores, puedes tomar ventaja de la característica de negociación de contenido |
|
||||||
provista por el comportamiento (behavior) [[yii\filters\ContentNegotiator|contentNegotiator]]. El comportamiento `contentNegotiator` |
|
||||||
definirá la propiedad [[yii\web\Response::acceptParams]] cuando determina qué tipo |
|
||||||
de contenido soportar. |
|
||||||
|
|
||||||
Por ejemplo, si una petición es enviada con la cabecera HTTP `Accept: application/json; version=v1`, |
|
||||||
después de la negociación de contenido, [[yii\web\Response::acceptParams]] contendrá el valor `['version' => 'v1']`. |
|
||||||
|
|
||||||
Basado en la información de versión contenida en `acceptParams`, puedes escribir código condicional en lugares |
|
||||||
como acciones, clases de recursos, serializadores, etc. para proveer la funcionalidad apropiada. |
|
||||||
|
|
||||||
Dado que por definición las versiones menores requireren mantener la compatibilidad hacia atrás, con suerte no tendrás demasiadas |
|
||||||
comprobaciones de versión en tu código. De otra manera, probablemente puede ocurrir que necesites crear una versión mayor. |
|
@ -1,29 +0,0 @@ |
|||||||
# Qué necesita saber |
|
||||||
|
|
||||||
La curva de aprendizaje de Yii no es tan empinada como en otros _frameworks_ en PHP, |
|
||||||
pero todavía hay algunas cosas que debería aprender antes de empezar con Yii. |
|
||||||
|
|
||||||
## PHP |
|
||||||
|
|
||||||
Yii es un _framework_ (base estructurada de desarrollo) en PHP, así que asegúrese de |
|
||||||
[leer y comprender la referencia del lenguaje](https://www.php.net/manual/es/langref.php). |
|
||||||
Al desarrollar con Yii deberá escribir código de manera orientada a objetos, así que |
|
||||||
asegúrese de estar familiarizado con |
|
||||||
[clases y objetos](https://www.php.net/manual/es/language.oop5.basic.php) así como con |
|
||||||
[espacios de nombres](https://www.php.net/manual/es/language.namespaces.php). |
|
||||||
|
|
||||||
## Programación orientada a objetos |
|
||||||
|
|
||||||
Se requiere una comprensión básica de la programación orientada a objetos. Si no está |
|
||||||
familiarizado con ella, diríjase a alguno de los muchos tutoriales disponibles, como |
|
||||||
[el de tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762). |
|
||||||
|
|
||||||
Observe que cuanto más complicada sea su aplicación, más conceptos avanzados de la |
|
||||||
POO deberá aprender para gestionar con éxito esa complejidad. |
|
||||||
|
|
||||||
## Línea de órdenes y composer |
|
||||||
|
|
||||||
Yii usa profusamente el gestor de paquetes _de facto_ de PHP, [Composer](https://getcomposer.org/), |
|
||||||
así que asegúrese de leer y comprender su [guía](https://getcomposer.org/doc/01-basic-usage.md). |
|
||||||
Si no está familiarizado con el uso de la línea de órdenes, es hora de empezar a probarla. |
|
||||||
Una vez que aprenda los fundamentos, nunca querrá trabajar sin ella. |
|
@ -1,20 +0,0 @@ |
|||||||
Tests de aceptación |
|
||||||
=================== |
|
||||||
|
|
||||||
Un test de aceptación verifica escenarios desde la perspectiva de un usuario. |
|
||||||
Se accede a la aplicación testeada por medio de PhpBrowser o de un navegador de verdad. |
|
||||||
En ambos casos los navegadores se comunican vía HTTP así que la aplicación debe ser |
|
||||||
servida por un servidor web. |
|
||||||
|
|
||||||
Los tests de aceptación se implementan con ayuda del _framework_ Codeception, que tiene |
|
||||||
una buena documentación: |
|
||||||
|
|
||||||
- [Codeception para el _framework_ Yii](https://codeception.com/for/yii) |
|
||||||
- [Tests funcionales de Codeception](https://codeception.com/docs/04-FunctionalTests) |
|
||||||
|
|
||||||
## Ejecución de tests en las plantillas básica y avanzada |
|
||||||
|
|
||||||
Si ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) |
|
||||||
para más detalles sobre la ejecución de tests. |
|
||||||
|
|
||||||
Si ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing). |
|
@ -1,23 +1,49 @@ |
|||||||
Preparación del entorno de pruebas |
Preparación del entorno de test |
||||||
================================== |
=============================== |
||||||
|
|
||||||
Yii 2 ha mantenido oficialmente integración con el _framework_ de testeo [`Codeception`](https://github.com/Codeception/Codeception), |
> Note: Esta sección se encuentra en desarrollo. |
||||||
que le permite crear los siguientes tipos de tests: |
|
||||||
|
|
||||||
- [Unitari](test-unit.md) - verifica que una unidad simple de código funciona como se espera; |
Yii 2 ha mantenido integración oficial con el framework de testing [`Codeception`](https://github.com/Codeception/Codeception), |
||||||
- [Funcional](test-functional.md) - verifica escenarios desde la perspectiva de un usuario a través de la emulación de un navegador; |
que te permite crear los siguientes tipos de tests: |
||||||
- [De aceptación](test-acceptance.md) - verifica escenarios desde la perspectiva de un usuario en un navegador. |
|
||||||
|
|
||||||
Yii provee grupos de pruebas listos para utilizar para los tres tipos de test, tanto en la plantilla de proyecto |
- [Test de unidad](test-unit.md) - verifica que una unidad simple de código funciona como se espera; |
||||||
[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) como en |
- [Test funcional](test-functional.md) - verifica escenarios desde la perspectiva de un usuario a través de la emulación de un navegador; |
||||||
[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced). |
- [Test de aceptación](test-acceptance.md) - verifica escenarios desde la perspectiva de un usuario en un navegador. |
||||||
|
|
||||||
Codeception viene preinstalado tanto en la plantilla de proyecto básica como en la avanzada. |
Yii provee grupos de pruebas listos para utilizar en ambos |
||||||
En caso de que no use una de estas plantillas, puede instalar Codeception ejecutando |
[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) y |
||||||
las siguientes órdenes de consola: |
[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced) templates de proyectos. |
||||||
|
|
||||||
|
Para poder ejecutar estos tests es necesario instalar [Codeception](https://github.com/Codeception/Codeception). |
||||||
|
Puedes instalarlo tanto localmente - únicamente para un proyecto en particular, o globalmente - para tu máquina de desarrollo. |
||||||
|
|
||||||
|
Para la instalación local utiliza los siguientes comandos: |
||||||
|
|
||||||
|
``` |
||||||
|
composer require "codeception/codeception=2.1.*" |
||||||
|
composer require "codeception/specify=*" |
||||||
|
composer require "codeception/verify=*" |
||||||
|
``` |
||||||
|
|
||||||
|
Para la instalación global necesitarás la directiva `global`: |
||||||
|
|
||||||
``` |
``` |
||||||
composer require codeception/codeception |
composer global require "codeception/codeception=2.1.*" |
||||||
composer require codeception/specify |
composer global require "codeception/specify=*" |
||||||
composer require codeception/verify |
composer global require "codeception/verify=*" |
||||||
``` |
``` |
||||||
|
|
||||||
|
En caso de que nunca hayas utilizado Composer para paquetes globales, ejecuta `composer global status`. Esto debería mostrar la salida: |
||||||
|
|
||||||
|
``` |
||||||
|
Changed current directory to <directory> |
||||||
|
``` |
||||||
|
|
||||||
|
Entonces agrega `<directory>/vendor/bin` a tu variable de entorno `PATH`. Ahora podrás utilizar el `codecept` en la línea |
||||||
|
de comandos a nivel global. |
||||||
|
|
||||||
|
> Note: la instalación global te permite usar Codeception para todos los proyectos en los que trabajes en tu máquina de desarrollo y |
||||||
|
te permite ejecutar el comando `codecept` globalmente sin especificar su ruta. De todos modos, ese acercamiento podría ser inapropiado, |
||||||
|
por ejemplo, si 2 proyectos diferentes requieren diferentes versiones de Codeception instaladas. |
||||||
|
Por simplicidad, todos los comandos relacionados a tests en esta guía están escritos asumiendo que Codeception |
||||||
|
ha sido instalado en forma global. |
||||||
|
@ -1,25 +1,11 @@ |
|||||||
Tests funcionales |
Tests Funcionales |
||||||
================= |
================= |
||||||
|
|
||||||
Los tests funcionales verifican escenarios desde la perspectiva de un usuario. |
> Note: Esta sección se encuentra en desarrollo. |
||||||
Son similares a los [tests de aceptación](test-acceptance.md) pero en lugar de |
|
||||||
comunicarse vía HTTP rellena el entorno como parámetros POST y GET y después ejecuta |
|
||||||
una instancia de la aplicación directamente desde el código. |
|
||||||
|
|
||||||
Los tests funcionales son generalmente más rápidos que los tests de aceptación y |
- [Tests Funcionales de Codeception](http://codeception.com/docs/04-FunctionalTests) |
||||||
proporcionan _stack traces_ detalladas en los fallos. |
|
||||||
Como regla general, debería preferirlos salvo que tenga una configuración de servidor |
|
||||||
web especial o una interfaz de usuario compleja en Javascript. |
|
||||||
|
|
||||||
Las pruebas funcionales se implementan con ayuda del _framework_ Codeception, que tiene |
Ejecutar test funcionales de templates básicos y avanzados |
||||||
una buena documentación: |
---------------------------------------------------------- |
||||||
|
|
||||||
- [Codeception para el _framework_ Yii](https://codeception.com/for/yii) |
Por favor consulta las instrucciones provistas en `apps/advanced/tests/README.md` y `apps/basic/tests/README.md`. |
||||||
- [Tests funcionales de Codeception](https://codeception.com/docs/04-FunctionalTests) |
|
||||||
|
|
||||||
## Ejecución de tests en las plantillas básica y avanzada |
|
||||||
|
|
||||||
Si ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) |
|
||||||
para más detalles sobre la ejecución de tests. |
|
||||||
|
|
||||||
Si ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing). |
|
||||||
|
@ -1,80 +0,0 @@ |
|||||||
Tests |
|
||||||
===== |
|
||||||
|
|
||||||
Las pruebas son una parte importante del desarrollo de software. Seamos conscientes |
|
||||||
de ello o no, ralizamos pruebas contínuamente. |
|
||||||
Por ejemplo, cuando escribimos una clase en PHP, podemos depurarla paso a paso o |
|
||||||
simplemente usar declaraciones `echo` o `die` para verificar que la implementación |
|
||||||
funciona conforme a nuestro plan inicial. En el caso de una aplicación web, introducimos |
|
||||||
algunos datos de prueba en los formularios para asegurarnos de que la página interactúa |
|
||||||
con nosotros como esperábamos. |
|
||||||
|
|
||||||
El proceso de testeo se puede automatizar para que cada vez que necesitemos verificar |
|
||||||
algo, solamente necesitemos invocar el código que lo hace por nosotros. El código que |
|
||||||
verifica que el restulado coincide con lo que habíamos planeado se llama *test* y el proceso |
|
||||||
de su creación y posterior ejecución es conocido como *testeo automatizado*, que es el |
|
||||||
principal tema de estos capítulos sobre testeo. |
|
||||||
|
|
||||||
|
|
||||||
## Desarrollo con tests |
|
||||||
|
|
||||||
El Desarrollo Dirigido por Pruebas (_Test-Driven Development_ o TDD) y el Desarrollo |
|
||||||
Dirigido por Corpotamientos (_Behavior-Driven Development_ o BDD) son enfoques para |
|
||||||
desarrollar software, en los que se describe el comportamiento de un trozo de código |
|
||||||
o de toda la funcionalidad como un conjunto de escenarios o pruebas antes de escribir |
|
||||||
el código real y sólo entonces crear la implementación que permite pasar esos tests |
|
||||||
verificando que se ha logrado el comportamiento pretendido. |
|
||||||
|
|
||||||
El proceso de desarrollo de una funcionalidad es el siguiente: |
|
||||||
|
|
||||||
- Crear un nuevo test que describe una funcionalidad a implementar. |
|
||||||
- Ejecutar el nuevo test y asegurarse de que falla. Esto es lo esperado, dado que todavía no hay ninguna implementación. |
|
||||||
- Escribir un código sencillo para superar el nuevo test. |
|
||||||
- Ejecutar todos los tests y asegurarse de que se pasan todos. |
|
||||||
- Mejorar el código y asegurarse de que los tests siguen superándose. |
|
||||||
|
|
||||||
Una vez hecho, se repite el proceso de neuvo para otra funcionalidad o mejora. |
|
||||||
Si se va a cambiar la funcionalidad existente, también hay que cambiar los tests. |
|
||||||
|
|
||||||
> Tip: Si siente que está perdiendo tiempo haciendo un montón de iteraciones pequeñas |
|
||||||
> y simples, intente cubrir más por cada escenario de test, de modo que haga más cosas antes |
|
||||||
> de ejecutar los tests de nuevo. Si está depurando demasiado, intente hacer lo contrario. |
|
||||||
|
|
||||||
La razón para crear los tests antes de hacer ninguna implementación es que eso nos permite |
|
||||||
centrarnos en lo que queremos alcanzar y sumergirnos totalmente en «cómo hacerlo» después. |
|
||||||
Normalmente conduce a mejores abstracciones y a un más fácil mantenimiento de los tests |
|
||||||
cuando toque hacer ajustes a las funcionalidades o componentes menos acoplados. |
|
||||||
|
|
||||||
Para resumir, las ventajas de este enfoque son las siguientes: |
|
||||||
|
|
||||||
- Le mantiene centrado en una sola cosa en cada momento, lo que resulta en una mejor planificación e implementación. |
|
||||||
- Resulta en más funcionalidades cubiertas por tests, y en mayor detalle. Es decir, si se superan los tests, lo más problable es que no haya nada roto. |
|
||||||
|
|
||||||
A largo plazo normalmente tiene como efecto un buen ahorro de tiempo. |
|
||||||
|
|
||||||
## Qué y cómo probar |
|
||||||
|
|
||||||
Aunque el enfoque de primero los tests descrito arriba tiene sentido para el largo plazo |
|
||||||
y proyectos relativamente complejos, sería excesivo para proyectos más simples. |
|
||||||
Hay algunas indicaciones de cuándo es apropiado: |
|
||||||
|
|
||||||
- El proyecto ya es grande y complejo. |
|
||||||
- Los requisitos del proyecto están empezando a hacerse complejos. El proyecto crece constantemente. |
|
||||||
- El proyecto pretende a ser a largo plazo. |
|
||||||
- El coste de fallar es demasiado alto. |
|
||||||
|
|
||||||
No hay nada malo en crear tests que cubran el comportamiento de una implementación existente. |
|
||||||
|
|
||||||
- Es un proyecto legado que se va a renovar gradualmente. |
|
||||||
- Le han dado un proyecto sobre el que trabajar y no tiene tests. |
|
||||||
|
|
||||||
En algunos casos cualquier forma de testo automatizado sería exagerada: |
|
||||||
|
|
||||||
- El proyecto es sencillo y no se va a volver más complejo. |
|
||||||
- Es un proyecto puntual en el que no se seguirá trabajando. |
|
||||||
|
|
||||||
De todas formas, si dispone de tiempo, es bueno automatizar las pruebas también en esos casos. |
|
||||||
|
|
||||||
## Más lecturas |
|
||||||
|
|
||||||
- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530. |
|
@ -1,25 +1,25 @@ |
|||||||
Pruebas unitarias |
Tests de Unidad |
||||||
================= |
=============== |
||||||
|
|
||||||
Un test unitario se encarga de verificar que una unidad simple de código funcione como se espera. |
> Note: Esta sección se encuentra en desarrollo. |
||||||
Esto decir, dados diferentes parámetros de entrada, el test verifica que el método |
|
||||||
de la clase devuelve el resultado esperado. |
|
||||||
Normalmente los tests unitarios son desarrollados por la persona que escribe las clases testeadas. |
|
||||||
|
|
||||||
Los tests unitarios en Yii están construidos en base a PHPUnit y, opcionalmente, Codeception, por lo que se recomienda consultar su respectiva documentación: |
Un test de unidad se encarga de verificar que una unidad simple de código funcione como se espera. En la programación orientada a objetos, |
||||||
|
la unidad de código más básica es una clase. Por lo tanto, un test de unidad necesita verificar que cada método de la interfaz de la clase funciona apropiadamente. |
||||||
|
Esto quiere decir que, dando diferentes parámetros de entrada, el test verifica que el método devuelve el resultado esperado. |
||||||
|
Los tests de unidad son normalmente desarrollados por la persona que escribe las clases siendo testeadas. |
||||||
|
|
||||||
- [Codeception para el _framework_ Yii](https://codeception.com/for/yii) |
Los tests de unidad en Yii están construidos en base a PHPUnit y opcionalmente, Codeception, por lo que se recomineda consultar su respectiva documentación: |
||||||
- [Tests unitarios con Codeception](https://codeception.com/docs/05-UnitTests) |
|
||||||
- [Documentación de PHPUnit, empezando por el capítulo 2](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html) |
|
||||||
|
|
||||||
## Ejecución de tests en las plantillas básica y avanzada |
- [Documentación de PHPUnit comienza en el capítulo 2](http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html). |
||||||
|
- [Tests de Unidad con Codeception](http://codeception.com/docs/05-UnitTests). |
||||||
|
|
||||||
Si ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) |
Ejecutar test de unidad de templates básicos y avanzados |
||||||
para más detalles sobre la ejecución de tests. |
-------------------------------------------------------- |
||||||
|
|
||||||
Si ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing). |
Por favor consulta las instrucciones provistas en `apps/advanced/tests/README.md` y `apps/basic/tests/README.md`. |
||||||
|
|
||||||
##Tests unitarios del framework |
Test de unidad del Framework |
||||||
|
---------------------------- |
||||||
|
|
||||||
Si desea ejecutar tests unitarios para el framework Yii en sí, consulte |
Si quieres ejecutar tests de unidad para Yii en sí, consulta |
||||||
«[Comenzando con el desarrollo de Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)». |
"[Comenzando a desarrollar con Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)". |
||||||
|
@ -1,7 +1,6 @@ |
|||||||
[ |
[ |
||||||
"Antonio Ramirez", |
"Antonio Ramirez", |
||||||
"Daniel Gómez Pan", |
"Daniel Gómez Pan", |
||||||
"Enrique Matías Sánchez (Quique)", |
|
||||||
"'larnu'", |
"'larnu'", |
||||||
"Luciano Baraglia" |
"Luciano Baraglia" |
||||||
] |
] |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue