Mysql, Propel et l’UTF8 sont dans un bateau
Un gros souci que j’ai toujours eu lors de mes projets symfony c’est la capacité de dire à Propel que je veux ma base en UTF8, j’ai beau mettre UTF8 un peu partout, database.yml, schema.yml, propel.ini rien n’y fait, mon build-all créé mes tables en ISO. D’ailleurs un peu déçu que Jobeet chapitre 3 n’aborde pas le sujet.
J’ai donc tenté de voir du côté de Mysql, pour mettre ce dernier par défaut en utf8, mais là aussi, on a beau remplir le my.cnf de utf8 dans toutes les options, rien de concret ne se produit.
J’en été donc resté à devoir rajouter à la main, dans mon .sql généré par symfony, le fameux « DEFAUT CHARSET=UTF8″. Mais devoir le rajouter à chaque modification de mon schema, cela peut devenir pénible dans des projets de tests.
Et puis par hasard au détour d’une conversation sur mysql dans une mailing-list (mea culpa j’ai pas noté l’adresse) je suis enfin tombé sur quelque chose qui fonctionne! A la création de la table, écrire:
CREATE DATABASE mydb CHARACTER SET utf8 COLLATE utf8_general_ci
Voilà, toutes les tables créés maintenant dans cette base seront en UTF8 par défaut.
Il y a sans doute mieux, mais pour l’instant je me contente de ça. Mais peut-être que quelqu’un a une meilleur solution? Comment gérez-vous vos tables en UTF8?
Tags: mysql, propel, Symfony, utf8
7 Réponses
Laisser un message

Nous avons rencontré le même problème avec sf1.0.x
En fait par défaut MySQL est en latin1 et du coup, nous avons une base en Latin1 avec des données en UTF8 dedans. Bref un beau bazard car dès que l’on a besoin d’accéder aux données autrement que par symfony, on a des problèmes.
J’ai finis par faire un batch qui converti les champs en latin en utf8 [1].
Je le donne au cas où:
http://fr.pastebin.ca/1431312
Comme je travaille toujours en UTF-8 je crée mes bases avec ce COLLATE et ça marche très bien. C’est la solution que tu as fini par trouver.
Il est important de noter en revanche que le collate du client de connexion (l’option « encoding » dans le databases.yml) est très très important lui aussi, car quelle que soit sa valeur on n’aura pas de problème apparent, sauf le jour où on change de système parce que le collate du client par défaut est défini au niveau du système, et il vaut donc mieux ne pas compter dessus (beaucoup de Windows le laisseront en latin1, beaucoup de Linux le passeront en utf8).
Si on a tout en utf8 (base, tables, page, chaines dans le code), mais que le client est en latin1, voici ce qui se produit lors d’un insert :
1. construction de la requête : chaine en utf8 (« été » = 5 octets)
2. le client reçoit la donnée, et l’envoie dans les tables : il est en latin1 et le champ cible est en utf8, donc il convertit.
3. Dans le champ de la table, on a « été » en utf8 (soit 9 octets).
Et voici ce qui se produit lorsqu’on récupère la donnée :
1. le client lit le champ de la table « été » (utf8, 9 octets).
2. il est en latin1, et le champ était en utf8, donc il convertit en « été » (utf8, 5 octets).
3. on reçoit la bonne chaine en utf8.
Du coup tout s’est passé de manière transparente, tout semble fonctionner correctement sauf que :
– le jour où le système change d’encodage c’est la merde (à la rigueur on respécifiera « latin1″ à ce moment là, mais avant de comprendre pourquoi en mettant « utf8″ ça ne corrige rien on va tourner un moment ;))
– le jour où on fait un restore de la base en ligne de commande, ça va foirer (parce que l’encoding par défaut du client se base sur l’encoding du shell courant, en général utf8).
– on a dans la base des chaines trop grosses puisqu’encodées une fois de trop en utf8.
Voilà, je profitais de cet article pour parler de ce problème souvent latent et insoupçonné, mais pourtant présent ;) Perso j’ai du corriger tous mes sites (pas que ceux en Symfony) car ils avaient tous ce problème vu que je ne spécifiais jamais l’encoding client.
Ah du coup, il faut aussi parler des solutions :
– Dans Symfony, spécifier « encoding: utf8″ dans les options du databases.yml.
– Dans les autres cas, la première requête après la connexion doit être « SET NAMES UTF8″.
Une fois ceci fait, on doit observer que toutes les chaines du site ont sauté. Si ce n’est pas le cas c’est que l’encoding client par défaut était déjà utf8 : tant mieux :)
Sinon, il faut faire un export de la base avec un client en encoding « latin1″ (on récupèrera alors les chaines en utf8 correct), puis faire une restauration avec un client en encoding « utf8″ :
$ mysqldump -u user -p –encoding=latin1 ma_base > dump.sql
$ mysql -u user -p –encoding=utf8 ma_base < dump.sql
@goulwen Merci bien! Ca mériterait d’être adapté en task 1.2 et mis dans un plugin. Ca me parait très utile!
@naholyr Grand merci pour ces explications, j’avoue que personnellement c’est le genre de truc qui m’intéresse beaucoup mois, du coup je suis tjrs content que des devs plus sages se risque à des explications claires ;)
Tu permets que j’étaye mon billet avec les informations supplémentaires que tu as apportées?
Avec grand plaisir c’est évidemment là pour ça :)
un de ces 4 faudra vraiment que je me fasse un blog :p
héhé oui, je me suis dis ça pendant 2ans avant de me lancer ^^
3 mois après … :)
J’ai eu aujourd’hui moi aussi le même problème, autant dire que les japonnais qui devaient remplir le site qu’on développe s’arrachaient les cheveux!
Et j’ai trouvé la solution:
dans le schema.yml il faut rajouter au début:
options:
type: INNODB
collate: utf8_unicode_ci
charset: utf8
et le tour est joué :)
C’est tout de même bizarre que malgré les configurations (database.yml, propel.ini …) ça ne se fasse pas par défaut!
Hello Benjamin,
Je suis effectivement tombé sur cette possibilité récemment! Ca fonctionne dans doctrine, mais je n’avais pas pu tester pour propel.
C’est chose fait donc! Encore un billet à mettre à jour.
Merci en tout cas!