Migracja Laravel: unikalny klucz jest za długi, nawet jeśli został określony


166

Próbuję przenieść tabelę użytkowników w Laravel. Kiedy uruchamiam migrację, pojawia się ten błąd:

[Illuminate \ Database \ QueryException] SQLSTATE [42000]: Błąd składni lub naruszenie zasad dostępu: 1071 Podany klucz był za długi; maksymalna długość klucza to 767 bajtów (SQL: alter table usersadd unique users_email_uniq ( email))

moja migracja wygląda następująco:

Schema::create('users', function(Blueprint $table)
{
    $table->increments('id');
    $table->string('name', 32);
    $table->string('username', 32);
    $table->string('email', 320);
    $table->string('password', 64);
    $table->string('role', 32);
    $table->string('confirmation_code');
    $table->boolean('confirmed')->default(true);
    $table->timestamps();

    $table->unique('email', 'users_email_uniq');
});

Po pewnym googlowaniu natknąłem się na ten raport o błędzie, w którym Taylor mówi, że możesz określić klucz indeksu jako drugi parametr unique(), co zrobiłem. Nadal daje błąd. Co tu się dzieje?


Dlaczego używasz 320 znaków w wiadomościach e-mail? To może być twój problem.
Antonio Carlos Ribeiro

1
To rzeczywiście był problem, nie mam pojęcia, dlaczego. Ale tak, masz rację, nie wiem, dlaczego określiłem długość znaków dla każdego pola. Usunęliśmy te ograniczenia
harryg

To zabawne, jak nikt nie zasugerował użycia pola o stałej długości, które zawiera hash wiadomości e-mail i voila - problem rozwiązany na zawsze, dla dowolnego frameworka i dowolnej relacyjnej bazy danych. Ponieważ w ten sposób gwarantujemy wyjątkowość - używając reprezentacji stałej liczby danych wejściowych o zmiennej długości, biorąc pod uwagę fakt, że zakres liczb jest wystarczająco duży (a dla sha1 / sha256 tak jest).
Uwaga:


Odpowiedzi:


280

Określ mniejszą długość wiadomości e-mail:

$table->string('email', 250);

Która jest właściwie domyślna:

$table->string('email');

I powinieneś być dobry.

Dla Laravel 5.4 możesz znaleźć rozwiązanie w tym Laravel 5.4: Określony klucz był zbyt długi błąd, post z Laravel News :

Jak opisano w przewodniku po migracji, aby to naprawić, wszystko, co musisz zrobić, to wyedytować plik AppServiceProvider.php i ustawić domyślną długość ciągu w metodzie rozruchu:

use Illuminate\Database\Schema\Builder;


public function boot()
{
    Builder::defaultStringLength(191);
}

6
Maksymalna możliwa długość wiadomości e-mail jest 254więc prawdopodobnie warta zapamiętania, więc w takim przypadku prawdopodobnie zweryfikowałbym wyjątkowość za pomocą walidatora.
Sebastian Sulinski

12
W przypadku Laravel 5.4 użyj \Illuminate\Database\Schema\Builder::defaultStringLength(191);poprawnej ścieżki odniesienia do funkcji
webcoder

5
Po wykonaniu konfiguracji w AppServiceProvider.php ten problem nadal występuje. Jestem po prostu zdezorientowany. Czemu? Zrestartowałem serwer, bazę danych i wszystko, ale nadal. Proszę pomóż.
Koushik Das

3
Musisz ustawić długość indeksowanej kolumny zgodnie z limitem 767 bajtów. Należy pamiętać, że VARCHAR może mieć 1, 2 lub 4 bajty na każdą jednostkę długości. Przykład: utf8_mb4 (4 bajty) -> 767/4 = 191. W przeciwnym razie utf8_general_ci dla VARCHAR (X) z X <85 (1 bajt) = O (85) lub utf8_general_ci dla VARCHAR (X) z X> = 86 (2 bajtów) -> 767/2 = 383. Rozważ także inne długości kolumn w indeksach wielu kolumn.
Jackie Degl'Innocenti

2
Możesz również chcieć bezpośrednio edytować długość określonej kolumny w plikach migracji, ponad określając domyślną długość dla wszystkich kolumn ciągów, ponieważ nie wszystkie kolumny będą wymagały tego ograniczenia, ponieważ nie znajdują się w żadnym indeksie. $ table-> string ('nazwa_kolumny', 191);
Jackie Degl'Innocenti

109

Zaktualizuj 1

Od wersji Laravel 5.4 te zmiany nie są już potrzebne.

Laravel 5.4 używa domyślnie zestawu znaków utf8mb4, który obejmuje obsługę przechowywania "emoji" w bazie danych. Jeśli aktualizujesz aplikację z Laravel 5.3, nie musisz przełączać się na ten zestaw znaków.

Zaktualizuj 2

Bieżące produkcyjne wersje MariaDB NIE obsługują tego ustawienia domyślnie na całym świecie. Jest on domyślnie zaimplementowany w MariaDB 10.2.2+ .

Rozwiązanie

A jeśli celowo chcesz używać poprawnego domyślnego ustawienia future (począwszy od Laravel 5.4) wielobajtowego utf8mb4wsparcia UTF8 dla 😀, zacznij naprawiać 😂 konfigurację bazy danych.

W Laravel config/database.phpzdefiniuj:

'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',

DYNAMICpozwala na przechowywanie długich indeksów kluczy .

Ustawienia serwera (domyślnie zawarte w MySQL 5.7.7+ / MariaDB 10.2.2+):

[mysqld]
# default character set and collation
collation-server = utf8mb4_unicode_ci
character-set-server = utf8mb4

# utf8mb4 long key index
innodb_large_prefix = 1
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = 1

Dla klientów:

[mysql]
default-character-set=utf8mb4

A następnie ZATRZYMAJ swój serwer MySQL / MariaDB. Po tym STARTU. Hot RESTART może nie działać.

sudo systemctl stop mysqld
sudo systemctl start mysqld

Teraz masz Laravel 5.x z obsługą UTF8.


3
Może to działać wystarczająco dobrze z MySQL 5.5 (nie próbowałem rekonfigurować). 5.7 (prawdopodobnie również 5.6) działało bez potrzeby rekonfiguracji. 5.7 była domyślną dystrybucją Community Server z podstawową konfiguracją.
Pjotr

Jak wspomniałeś, zmieniłem silnik w pliku database.php, ale nadal tworzy on tabele z row = compact, co powoduje problem. Nie do końca zrozumiałem, czy mówisz, że nie wystarczy dokonać tej zmiany w pliku database.php i że jest to również wymagane do wprowadzenia zmian w pliku my.cnf?
vesperknight

Wystarczy wprowadzić zmiany w database.phppliku konfiguracyjnym i wpłynie to na lokalny projekt Laravel. Upewnij się, że deletebaza danych jest przed wprowadzeniem zmian i utwórz ją z nowymi ustawieniami. Musisz zmienić my.cnfplik konfiguracyjny tylko w przypadku globalnych zmian po stronie serwera (obecnie używane są wszystkie nowe instalacje utf8mb4).
strzelec

Lub - dodaj kolejne pole, oblicz hash wiadomości e-mail, nadaj mu unikalność, rozwiąż problem na zawsze, unikaj majstrowania przy zmiennych inicjalizacyjnych bazy danych.
Uwaga:

Jeśli ktoś używa Doctrine 2 , możesz ustawić ROW_FORMAT, przekazując options={"row_format"="DYNAMIC"}do swojej @Tableadnotacji.
Albert221,

50

Jeśli jesteś włączony lub zaktualizowany do Laravel 5.4 To zadziałało dla mnie;

Tylko 1 zmiana. w AppServiceProvider.php

use Illuminate\Support\Facades\Schema;

public function boot()
{
  Schema::defaultStringLength(191);
}

Jak wspomniano w przewodniku migracji https://laravel.com/docs/master/migrations#creating-indexes


Zrobiłem to na samej migracji.
Amirmasoud

7
Dzieje się tak (prawdopodobnie) dlatego, że każdy znak zajmuje dokładnie 4 bajty, a maksymalna długość klucza jest mierzona w bajtach, a nie w znakach. Tak więc długość klucza będzie wynosić 191 * 4 = 764 bajty, tylko niewielki rozmiar poniżej maksymalnych 767 bajtów obsługiwanych przez bazę danych. Rozwiązania wymagają wyjaśnień IMO, jeśli mają przyczynić się do dzielenia się tutaj wiedzą, a nie tylko zapewnić „procedury”. Ale mimo to przydatna poprawka.
Jason

33

Jeśli ktoś inny natknie się na tę odpowiedź, tak jak ja, ale z innego powodu, możesz sprawdzić swój zestaw znaków / zestawień Laravel DB.

Instalowałem aplikację (Snipe-IT) i skonfigurowałem konfigurację bazy danych Laravel tak, aby korzystała z następujących elementów:

'charset' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',

Usunięcie mb4obu łańcuchów rozwiązało problem, chociaż uważam, że odpowiedź Antonio jest prawdziwym rozwiązaniem problemu.



16

Usuń mb4 z charset i collation z config / database.php, a następnie uruchomi się pomyślnie.
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',


15

Dla laravel 5.6
To rozwiązanie rozwiązuje mój problem,
przejdź do config/database.php
Znajdowanie kodu poniżej

'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],

Zmień to dwa pola

'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci'

Z tym

'charset' => 'utf8',
'collation' => 'utf8_unicode_ci'

14

Napotkałem ten sam problem i naprawiłem go, dodając poniższe dwie linie do mojego pliku app / database.php

'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',

Mój plik wygląda jak poniżej:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Database Connection Name
    |--------------------------------------------------------------------------
    |
    | Here you may specify which of the database connections below you wish
    | to use as your default connection for all database work. Of course
    | you may use many connections at once using the Database library.
    |
    */

    'default' => env('DB_CONNECTION', 'mysql'),

    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',

    ......

2
Jeśli zainstalowałeś nową wersję w Laravel. Usuń „mb4” z utf8mb4 i utf8mb4_unicode_ci & config / database.php
Muthu17

13

W przypadku laravel 5.4 po prostu edytuj plik

App \ Providers \ AppServiceProvider.php

use Illuminate\Support\Facades\Schema;

public function boot()
{
    Schema::defaultStringLength(191);
}

7
File: config/database.php
change the following
FROM ->
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',

TO ->
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',

1
Chociaż może to odpowiedzieć na pytanie, lepiej dodać opis, w jaki sposób ta odpowiedź może pomóc w rozwiązaniu problemu. Przeczytaj artykuł Jak napisać dobrą odpowiedź, aby dowiedzieć się więcej.
Roszana Pitigala,

6

Miałem ten sam problem i używam wampa

Rozwiązanie: Otwórz plik: config / database.php

'engine' => null, => 'engine' => 'InnoDB',

Dzięki


Żadna z poprzednich odpowiedzi nie działała dla mnie, ale ta zadziałała jak urok! I to ma sens, uważam, że w poprzednich wersjach silnik Laravel DB był domyślnie ustawiony na InnoDB, więc wcześniej nie napotkaliśmy tych błędów.
Sasa Blagojevic

tak, potrzebowałem zrobić kilka innych odpowiedzi i to też.
Andrew,

6

W pliku config / database.php, gdzie:

'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',

Zmień ten wiersz na ten:

'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',

5

Dla laravel> = 5,6 użytkownicy

Otwórz AppServiceProvider.phpplik

Użyj następującej klasy

use Illuminate\Support\Facades\Schema;

Następnie wewnątrz bootmetody dodaj następujący wiersz

public function boot()
{
    Schema::defaultStringLength(191);
}

4

Dodałem do samej migracji

Schema::defaultStringLength(191);
Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

tak, wiem, że muszę wziąć to pod uwagę przy każdej migracji, ale wolałbym to niż schowanie go w jakimś zupełnie niepowiązanym dostawcy usług


4

Jeśli ktoś ma ten problem nawet po zrobieniu, powyższe zmiany. Na przykład w moim przypadku dokonałem poniższych zmian,

namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
  /**
   * Register any application services.
   *
   * @return void
   */
   public function register()
   {
      //
   }

   public function boot()
   {
     Schema::defaultStringLength(191);
   }
}

Ale to nie zadziała od razu z dwóch powodów. Po pierwsze, jeśli używasz lumen zamiast laravel, być może będziesz musiał najpierw odkomentować tę linię w pliku app.php.

$app->register(App\Providers\AppServiceProvider::class);

Następnie musisz ponownie utworzyć skrypt migracji za pomocą polecenia artisan,

php artisan make:migration <your_table_name>

Od teraz będą działać tylko zmiany wprowadzone w ServiceProvider.


4

dla laravel 5.7 napisz ten kod w pliku appserviceprovider.php

  use Illuminate\Support\Facades\Schema;

public function boot()
{
  Schema::defaultStringLength(191);
}

2

Zmień zestaw znaków z „utf8mb4” na „utf8” i

sortowanie do „utf8mb4_unicode_ci” do „utf8_unicode_ci”

w pliku config / database.php

U mnie to zadziałało.


2

Laravel domyślnie używa utf8mb4zestawu znaków, który obejmuje obsługę przechowywania "emotikonów" w bazie danych. Jeśli używasz wersji MySQL starszej niż wersja 5.7.7 lub MariaDB starszej niż wersja 10.2.2, może być konieczne ręczne skonfigurowanie domyślnej długości łańcucha generowanego przez migracje, aby MySQL mógł utworzyć dla nich indeksy. Możesz to skonfigurować, wywołując Schema::defaultStringLengthmetodę w swoim AppServiceProvider:

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

Możesz się wymeldować

https://laravel-news.com/laravel-5-4-key-too-long-error https://laravel.com/docs/5.5/migrations#indexes


dzięki, działa dla mnie. mamp mysql with laravel 5.6.23
bluesky

2

Dzieje się tak, ponieważ Laravel 5.4 używa utf8mb4, który obsługuje przechowywanie emoji.

Dodaj to do swojej aplikacji \ Providers \ AppServiceProvider.php

use Illuminate\Support\Facades\Schema;

public function boot()
{
    Schema::defaultStringLength(191);
}

i powinieneś być gotowy.


2

Jeśli jesteś włączony lub zaktualizowany do Laravel 5.4 i najnowszej wersji, to działa;
Tylko 1 zmiana w AppServiceProvider.php

use Illuminate\Support\Facades\Schema;

public function boot()
{
  Schema::defaultStringLength(191);
}

1

Chciałbym zwrócić uwagę na coś, co przegapiłem ...

Jestem nowy w Laravel i nie skopiowałem "use Illuminate .....", ponieważ naprawdę nie zwróciłem uwagi, ponieważ tuż nad funkcją boot masz już instrukcję use .

Mam nadzieję, że to pomoże każdemu

**use Illuminate\Support\Facades\Schema;**

public function boot()
{
    Schema::defaultStringLength(191);
}

Możesz również po prostu poprzedzić dowolną elewację\
Ohgodwhy

1

Miałem problem, zmień konfigurację 'config / database'

file 'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',

zachowanie tego samego wzorca w bazie danych.

Następnie wydałem polecenie

php artisan migrate

1

24 października 2016 Taylor Otwell ogłosił na Twitterze autorem Laravel

"utf8mb4" będzie domyślnym zestawem znaków MySQL w Laravel 5.4, aby zapewnić lepszą obsługę emoji. 🙌 Taylor Otwell Post na Twitterze

którym przed wersją 5.4 był zestaw znaków utf8

W tym stuleciu wiele aplikacji internetowych zawiera czat lub jakąś platformę umożliwiającą użytkownikom rozmowę, a wiele osób lubi używać emotikonów lub emotikonów. i to są jakieś super bohaterów, które wymagają więcej miejsca do przechowywania i że będzie to możliwe tylko przy użyciu utf8mb4jako charset . To jest powód, dla którego migrują utf8mb4tylko w celach kosmicznych.

jeśli spojrzysz w górę w Illuminate\Database\Schema\Builderklasie, zobaczysz, że $defaultStringLengthjest ustawiona na 255 , i aby zmodyfikować, możesz przejść przez SchemaFacade i wywołać defaultStringLengthmetodę i przekazać nową długość.

aby wykonać tę zmianę, wywołaj tę metodę w swojej AppServiceProviderklasie, która znajduje się w podkatalogu app \ provider, tak jak to

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // all other code ...

        Schema::defaultStringLength(191); 
    }
    // and all other code goes here
}

Zasugeruję użycie 191 jako wartości tylko dlatego, że MySQL obsługuje 767 bajtów i dlatego, że 767 / 4jest to liczba bajtów zajmowana przez każdy otrzymany znak wielobajtowy 191.

Możesz dowiedzieć się więcej tutaj Zestaw znaków utf8mb4 (4-bajtowe kodowanie Unicode UTF-8) Limity liczby kolumn tabeli i rozmiaru wiersza


To jedyna odpowiedź, która wyjaśnia 191magiczną liczbę.
Illya Moskvin


1

Przechodzenie do twojego config/database.phpi zmiana zestawu znaków i sortowania z utf8mb4 na utf8

'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',

Mój problem został rozwiązany tą metodą, powodzenia stary!


0

Nie będziesz mieć tego problemu, jeśli używasz MySQL 5.7.7+ lub MariaDB 10.2.2+.

Aby zaktualizować MariaDB na komputerze Mac za pomocą Brew, najpierw odłącz bieżącą: brew unlink mariadba następnie zainstaluj wersję deweloperską za pomocąbrew install mariadb --devel

Po zakończeniu instalacji zatrzymaj / uruchom uruchomioną usługę: brew services stop mariadb brew services start mariadb

Bieżąca wersja deweloperska to 10.2.3. Po zakończeniu instalacji nie będziesz już musiał się tym martwić i możesz używać utf8mb4 (który jest teraz domyślny w Laravel 5.4) bez przełączania się z powrotem na utf8 ani edytowania AppServiceProvider, jak zaproponowano w dokumentacji Laravel: https: // laravel .com / docs / master / release # laravel-5.4 (przewiń w dół do: Migration Default String Length )


0

Właśnie zainstalowałem MariaDB 10.2.4 RC, uruchomiłem nowy pusty projekt Laravel 5.4 i działa domyślna migracja (kolumny varchar (255)).

Nie ma potrzeby zmiany DB conf i Laravael config/database.php. Tak więc, jak zauważył @scorer o domyślnym zachowaniu w wersji 10.2.2+.


0

Wszystko zostało dobrze opisane w innych Anwser, więcej szczegółów znajdziesz w linku poniżej (szukaj za pomocą klucza „Długość indeksu i MySQL / MariaDB”) https://laravel.com/docs/5.5/migrations

ALE Cóż, nie o to chodzi w tej odpowiedzi! Chodzi o to, że nawet wykonując powyższe , polubisz kolejny błąd (wtedy lubisz php artisan migratepolecenie uruchomienia i ze względu na problem z długością, operacja utknęła w środku. rozwiązanie jest poniżej , a tabela użytkownika jest jak utworzona bez reszta lub nie do końca poprawnie) musimy rzucić bac k. domyślne wycofanie nie zrobi. ponieważ operacja migracji nie lubiła zakończenia. musisz ręcznie usunąć nowo utworzone tabele w bazie danych.

możemy to zrobić za pomocą majsterkowania jak poniżej:

L:\todos> php artisan tinker

Psy Shell v0.8.15 (PHP 7.1.10  cli) by Justin Hileman

>>> Schema::drop('users')

=> null

Sam miałem problem z tabelą użytkowników.

po tym możesz już iść

php artisan migrate:rollback

php artisan migrate

0

Ustaw silnik bazy danych InnoDB:

  Schema::create('users', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

0

Jeśli wypróbowałeś wszystkie inne odpowiedzi i nie zadziałały, możesz usunąć wszystkie tabele z bazy danych, a następnie wykonać polecenie migracji wszystkie naraz, używając tego polecenia:

php artisan migrate:fresh
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.