I'm newbie to Yii. Official documentation does not give any examples for CDbMessageSource.
Questions:
1) How/Where do I set CDbMessageSource as my MessageSource ?
2) In my current application I store Categories in one table and translations for Categories in other table. Tables structure:
CATEGORY
----------
cat_id (PK)
CATEGORY_TRANSLATION
--------------------
cat_id (FK)
en
ru
Now if I introduce sub-categories I would model DB this way:
SUB_CATEGORY
------------
sub_cat_id (PK)
cat_id (FK)
SUB_CATEGORY_TRANSLATION
------------------------
sub_cat_id (FK)
en
ru
Do I understand it correctly that in Yii if I want to use CDbMessageSource to store translations then I would need to merge CATEGORY & SUB_CATEGORY in to one table , then merge CATEGORY_TRANSLATION & SUB_CATEGORY_TRANSLATION in to other so that in result I get following structure (taken from here http://www.yiiframework.com/doc/api/1.1/CDbMessageSource) :
CREATE TABLE SourceMessage
(
id INTEGER PRIMARY KEY,
category VARCHAR(32),
message TEXT
);
CREATE TABLE Message
(
id INTEGER,
language VARCHAR(16),
translation TEXT,
PRIMARY KEY (id, language),
CONSTRAINT FK_Message_SourceMessage FOREIGN KEY (id)
REFERENCES SourceMessage (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
Thank you !
How to enable CDbMessageSource
The message source is an application component with the name "messages". Therefore you configure it just like any other component in your application configuration file:
array(
......
'components'=>array(
......
'messages'=>array(
'class'=>'CDbMessageSource',
// additional parameters for CDbMessageSource here
),
),
),
)
The message source and localizable models -- not an ideal relationship
It's important to keep in mind that the message source only provides translations for known messages. It does not make much sense to involve the message source in your model localization because how would you utilize it?
Assume you have a category with id = 1. How would you get its localized title? Something like Yii::t('category', 'title_'.$category->id) could work, but it's somewhat clumsy (not desirable syntax, you have to "bake in" your primary key information into your display code, etc). If your title localizations are also meant to be modifiable by users this is going to get even more complicated. (In any case, if you wanted to do this then merging the two translation tables and using a separate value when populating SourceMessage.category would be the way to go).
An alternative approach to localizing models
Here's a brief rundown of how you can conveniently localize your models. Let's say we have a Room model with a localizable name property. You can create a new table named LocalizedString and the corresponding model that has a structure similar to this:
CREATE TABLE IF NOT EXISTS `localized_string` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`LocaleCode` char(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`StringTemplate` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`Id`,`LocaleCode`),
);
Then, configure your Room model with a relation on LocalizedString:
public function relations()
{
$localeCode = Yii::app()->getLanguage();
return array(
'nameStringTemplate' => array(
self::BELONGS_TO, 'LocalizedString', 'NameStringTemplateId',
'condition' => 'nameStringTemplate.LocaleCode = \''.$localeCode.'\''),
);
}
And add a read-only property:
public function getName() {
return $this->nameStringTemplate->StringTemplate;
}
By doing this, you can now write $room->name anywhere and you will automagically get back the localized translation for the application's current language.
There are many details that need to be taken care of and that I have glossed over here, but the idea should be apparent.
Related
We had developed in the past some sites, from company presentation sites to eshops, in classic asp. All of these was developed in multilingual environment (el, en) most of them. From database view we had choose the following schema. For example, for products table we have two related tables one with no lingual depended fields and one for lingual depended fields with one to many relation.
CREATE TABLE ref_language (
Code Char(2)NOT NULL,
Name Varchar(20) NOT NULL,
PRIMARY KEY (Code)
);
CREATE TABLE app_product (
Id Int IDENTITY NOT NULL,
PRIMARY KEY (Id)
);
CREATE TABLE app_product_translation (
ProductId Int NOT NULL,
LanguageCode Char(2) NOT NULL,
Description Text NOT NULL,
FOREIGN KEY (ProductId) REFERENCES app_product(Id),
FOREIGN KEY (LanguageCode) REFERENCES ref_language(Code)
);
To recreate the product model we use stored procedures to join the two tables for the requested language.
Now we want to move into dot.net mvc model. And we are wondering if there is a better approach, most suitable in mvc model.
It depends on your requirements, but you can just create a class to load and cache the captions in memory, loading them using EF. Either use a static class or preferably the ASP.NET cache.
If you are doing dynamic stuff on the client side then expose the strings through a MVC of WebAPI controller if necessary.
To make this easier I would decouple your translations from products in the schema. Make your translations universal.
Just have a single table called app_translation with Id, LanguageCode and Translation field. Then reference the Id on any table that needs a translated caption.
To enforce referential integrity, you can also have a app_translation_identifier table with a single column and a unique constraint. Then FK from the app_translation.Id to app_tranlsation_identifier.Id. And also have a unique key on app_translation for the Id and LanguageCode.
I have some problems working with model first many to many relationship. Since I created many-many relationship between Town and Author via interface builder it created table TownAuthor with keys Towns_TownID and Authors_AuthorID but I want that just to be called TownID and AuthorID, how do I change that?
In Code first I would use that modelBuilder configuration in Context but I have no idea how to do this via model first...
You have to change the names of these columns in the Entity Designer DDL script (which is generated from the EDMX file and has ModelName.edmx.sql name) before executing it.
-- Creating table 'TownAuthor'
CREATE TABLE [dbo].[TownAuthor] (
[TownID] int NOT NULL,
[AuthorID] int NOT NULL
);
GO
I have a domain class called FoapRequest. I want one of the properties called "approver" to be a list of integers. Order matters, so I've defined the class as described by http://grails.org/doc/latest/guide/GORM.html#sets,ListsAndMaps as a list:
class FoapRequest {
Integer requester
Integer subject
List approver
static hasMany = [foap:FOAP, newFoap:NewFoap, approver:Integer]
...
Just for clarification, FOAP and NewFoap are two other domain objects.
I need to map this class to a particular table in the Oracle database, so I also specify a static mapping with a join table:
static mapping = {
table 'OUR_SCHEMA.FOAP_REQUEST_TABLE
id column : 'ID', generator:'sequence', params: [sequence:'OUR_SCHEMA.FOAP_REQUEST_SEQ']
requester column : 'REQUESTER'
subject column : 'SUBJECT'
approver indexColumn: [name: "APPROVER_IDX"], generator:'sequence', params: [sequence:'OUR_SCHEMA.APPROVER_SEQ'],
joinTable: [name:'OUR_SCHEMA.APPROVER_TABLE',
key: 'ASSOCIATED_REQUEST',
column: 'APPROVER_PIDM',
type: "integer"
]
However, when I try to create a new instance of the FoapRequest object, I get the following error:
Invalid column type
The console displays the following:
Error 2012-08-01 12:29:31,619 [http-bio-8080-exec-9] ERROR errors.GrailsExceptionResolver - SQLException occurred when processing request: [POST] /FOAPauth/foapRequest/saveFoapRequests - parameters:
I am certain that the issue lies with the jointable. The domain model didn't include the joinTable originally- approver was just an Integer type (I realized too late that I was going to need to track multiple approvers).
Here's the SQL for creating the APPROVERS table:
CREATE TABLE "OUR_SCHEMA"."APPROVER_TABLE"
(
"APPROVER_IDX" NUMBER(*,0) NOT NULL ENABLE,
"ASSOCIATED_REQUEST" NUMBER(*,0) NOT NULL ENABLE,
"APPROVER_PIDM" NUMBER(8),
);
I'd prefer to avoid creating an Approver domain class if at all possible, since all I really need to keep track of are the integer identifiers.
So, after much janking with join tables, I determined that the best way to deal with my needs was to simply create an Approver object in my domain model.
class Approver {
Integer pidm
String approvalDecision
Date lastUpdated
Date dateCreated
static belongsTo = [foap: FOAP]
}
To be honest, I'm not really sure why I was trying so hard to avoid this. Possibly because my DBAs use a version control system for table definitions that I find a hair annoying. :)
Regardless, a simple one-to-many relationship between domain classes met all my needs, no join table required.
For those who are still burning to know, I did manage to get a statically mapped join table working using a Map, which was more appropriate for my needs (though not as appropriate for them as a new domain class, and not nearly as simple).
I ended up doing it in a different domain object- FOAP instead of FoapRequest:
import java.util.Map
class FOAP {
...
Map approvalData
...
static mapping = {
table 'OURSCHEMA.FOAP_TABLE'
id column : 'ID', generator:'jpl.hibernate.util.TriggerAssignedIdentityGenerator'
fund column : 'FUND'
org column : 'ORG'
chartOfAccounts column : 'CHART_OF_ACCOUNTS'
permissionType column: 'PERMISSION_TYPE'
foapRequest column: 'REQUEST_ID'
version column : 'VERSION'
approvalData joinTable: [name:'OURSCHEMA.FOAP_APPROVERS',
key: 'FOAP'
]
}
For the table definition, I used the column names similar to those in my original question.
CREATE TABLE "OUR_SCHEMA"."APPROVER_TABLE"
(
"FOAP" NUMBER(*,0) NOT NULL ENABLE,
"APPROVER_IDX" VARCHAR2(255),
"APPROVER_DLT" NUMBER(8),
);
The IDX column was the map object's key, the DLT column its value. I'd recommend against this approach, for anyone who can avoid it. Creating a new domain object is much simpler.
I have two tables with a common primary key. Now i want to get data from that both tables and show in single view using that primary key.
How i can get both table data in single domain class? How can i specify mapping?
For Example
Table-A and Table-B both are in single schema ABC
class X {
int id
String name
static mapping = {
table name: "Table-A", schema: "ABC"
columns {
name column:'name'
}
}
}
now i want to get address from table-B so that my view looks like below
ID NAME ADDRESS
2 HSJHD 23 X-Street Washington USA
How to get two table data in single domain class?
This sounds like a foreign key relation, you would simply use belongsTo in each object (provided a one-to-one relationship).
http://grails.org/doc/latest/ref/Domain%20Classes/belongsTo.html
Otherwise you could create a database view on your database, then create a domain object to match that view. Creating a domain based on a database view is identical to creating a domain based on a table.
Few options
Just use hql to query and join on the primary key
Create a view from the two tables, and map new table to that view
Use belongs to and when u access one object then access the other
You will need to use constraints: http://www.grails.org/doc/2.0.x/ref/Constraints/Usage.html
and http://www.grails.org/doc/2.0.x/guide/single.html#constraints
I have three domain classes: Beer, Review, and Reviewer.
I want the Review table to create a many to many relationship between Beer and Reviewer, so I want the primary key of Review to be a composite of the id fields from Beer and Reviewer. I'm following this Grails documentation.
http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20(GORM).html#5.5.2.5%20Composite%20Primary%20Keys
Here are my domain classes.
class Beer {
String name
String type
Brewery breweryId
static hasMany = [ reviews : Review ]
static constraints = {
}
}
class Reviewer {
String screenName
static hasMany = [ reviews : Review ]
static constraints = {
}
}
class Review implements Serializable {
int score
Beer beer
Reviewer reviewer
static constraints = {
}
static mapping = {
id composite:['beer', 'reviewer']
}
}
I was getting compilation errors, but another answer here on stackoverflow said I needed to add implements Serializable. That took care of the error, but when I look in the database, I'm still not getting a composite primary key.
Here is what I'm seeing when I look at the table definition. I'm using Postgres.
Table "public.review"
Column | Type | Modifiers
-------------+---------+-----------
id | bigint | not null
version | bigint | not null
beer_id | bigint | not null
reviewer_id | bigint | not null
score | integer | not null
Indexes:
"review_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fkc84ef75823f39326" FOREIGN KEY (beer_id) REFERENCES beer(id)
"fkc84ef7587c483106" FOREIGN KEY (reviewer_id) REFERENCES reviewer(id)
I'd be happy with just a composite index with a unique constraint, but I can't figure out how to do that, either. I've been able to make a non-unique composite index, but this has two problems. One, it's non-unique. Two, the columns are specified in alphabetical order in the index (beer_id, reviewer_id). I'd like to specify the order of the columns in the index.
I have implemented a similar situation, with some different conditions:
There's no hasMany relationship.
Query to the join class is done by HQL
Using a more detailed mapping
When implementing like this, the mysql database is ok. (beer_id,reviewer_id) is the primary key.
class Review implements Serializable {
Beer beer
Reviewer reviewer
static Review get(long beerId, long reviewerId) {
find 'from Review where beer.id=:beerId and reviewer.id=:reviewerId',
[beerId: beerId, reviewerId: reviewerId]
}
static boolean remove(Beer beer, Reviewer reviewer, boolean flush = false) {
Review instance = Review.findByBeerAndReviewer(beer, reviewer)
instance ? instance.delete(flush: flush) : false
}
...
static mapping = {
table "REVIEW"
id composite: ['beer', 'reviewer']
beer(column: "beer_ID")
reviewer(column: "reviewer_ID")
version false
}
}
I don't know what exactly causes your problem, but hope this gives you some hint about where the problem can be.
I took the Grails mandate that I shouldn't use Composite Primary keys as wise advice and am avoiding it. If so, I believe a viable alternative to solve your problem is the Composite Unique Constraint.
ref: http://grails.org/doc/1.1.x/ref/Constraints/unique.html
Jacco's answer seems to be not correct, although it looks visually very close to correct, here is how you'd write a composite unique constraint for this problem:
static constraints = {
beer(unique: 'reviewer')
}
whereas if the programmer wanted to link 3 db fields as unique, the correct formation is:
static constraints = {
beer(unique: ['anotherField','reviewer'])
}
which looks like Jacco's answer, but the class name is not used as the first string of the constraint, the first field name is used.
I've just used this code structure in my own project app, and it seems to be behaving correctly, see also this on how to unit test unique constraints:
http://www.ibm.com/developerworks/java/library/j-grails10209/index.html
(see listing 11)
Try this, In your domain Review domain class:
static constraints = {
review(unique: ['beer','reviewer'])
}
you might need to drop the table and let Gorm recreate it.
the above constraint means that a review must consist of a unique record of beer/reviewer combination. it is still many to many where a reviewer has multiple beers and visa versa but the reviews are unique.