Reputation: 760
This post has 3 updates and may be long. Please see Original post section.
Annonce
codeNote that this is my current code. I have updated the url()
method and commented everything inside afterCreate()
. But the code is still the same. (sorry, it's just copy-and-paste, so commentaries are in french language).
/**
* Annonce.js
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
tableName: 'annonce',
autoPK: true,
attributes: {
qte_commande: {
type: 'integer',
defaultsTo: 1,
required: true
},
date_mise_en_ligne: 'date',
taille_echantillon: {
type: 'string',
enum: ['P', 'M', 'G'] // TODO : entité
},
qte_echantillons_ini: {
type: 'integer',
required: true,
defaultsTo: 1
},
qte_echantillons: {
type: 'integer'
},
titre_annonce: {
type: 'text',
required: true
},
texte_annonce_md: {
type: 'text',
required: true
},
slug: {
type: 'string',
unique: true
},
prix: {
type: 'float',
required: true
},
/*
* Associations
*/
utilisateur: {
model: 'utilisateur'
},
variete: {
model: 'variete'
},
echantillons: {
collection: 'echantillon',
via: 'annonce'
},
/*
* Méthode d'instance
*/
url: function(bool_url_courte) {
// Si la variété n'est pas accessible, on ne peut pas connaître son URL.
// Dans ce cas il y a un problème, mais on peut toujours donner l'url courte.
if (!this.variete) {
sails.log.warn(new Error('Variété non accessible depuis Annonce.url().'));
bool_url_courte = true;
}
// Si le slug n'existe pas, on le génère à la volée.
if (!this.slug) {
var slug = Variete.genSlug(this.titre_annonce);
// On va remplir le champ slug, qui doit être unique
sails.log.verbose('Après la création d\'annonce : ' + this.id);
sails.log.verbose('Le slug vaut : ' + slug);
// On va donc rechercher une annonce qui comporte
// déjà un tel slug.
// Si on ne trouve pas, on pourra faire
// attrs.slug = slug;
// Si on trouve quelque chose, alors il faudra allouer
// attrs.slug = slug + '_' + attrs.id (ce qui est le seul
// moyen facile pour obtenir une chaîne unique).
// On copie this vers ann_orig car this passe à undefined dans
// Annonce.findOne().
var ann_orig = this;
Annonce.findOne({slug: slug}, function(err1, ann) {
if (err1) {
sails.log.error(err1);
// Erreur de communication avec la BDD.
// On renvoie vers /erreur
return '/erreur';
} else if (!ann) {
// Chouette, c'est la première annonce avec ce titre.
sails.log.verbose('C\'est la première annonce de ce nom : ' + ann_orig.titre_annonce);
ann_orig.slug = slug;
ann_orig.save(function(err2) {
if (err2) {
sails.log.error('Erreur durant la sauvegarde de annonce.slug : ' + ann_orig.id + ' / ' + ann_orig.slug, err2);
// On renvoie quand-même vers /erreur
return '/erreur';
} else {
sails.log.info('Annonce /a/' + ann_orig.id + ' <---> slug = ' + ann_orig.slug);
}
});
} else {
// Malheur, on a trouvé quelque chose, donc ce
// slug n'est pas unique.
sails.log.verbose('Il existe une autre annonce avec ce titre : ' + ann_orig.titre_annonce);
ann_orig.slug = slug + '_' + ann_orig.id;
ann_orig.save(function(err4) {
if (err4) {
sails.log.error('Erreur durant la sauvegarde de annonce.slug : ' + ann_orig.id + ' / ' + ann_orig.slug, err4);
} else {
sails.log.info('Annonce /a/' + ann_orig.id + ' <---> slug = ' + ann_orig.slug);
}
});
}
});
} // fin génération du slug.
// Si la variété n'a pas de slug ou si on veut une url courte
// alors on renvoie /a/:id
// Sinon l'url est /annonce/variete.slug/annonce.slug
//
// TODO IMPORTANT : Lors du premier appel à cette fonction, il y a
// un update en base de donnée fait de manière asynchrone. Du coup,
// this.slug n'est peut-être pas encore généré. Il le sera la prochaine fois.
if (!this.slug || !this.variete.slug || bool_url_courte){
return '/a/' + this.id;
} else {
// L'annonce a ce qu'il faut
return '/annonce/' + this.variete.slug + '/' + this.slug;
}
}
},
/*
* beforeCreate() : on met à jour :
* - qte_echantillons
*
* TODO IMPORTANT : le slug n'existe pas encore. En effet, slug doit être unique,
* or pour être unique il faut pouvoir s'assurer que titre_annonce est unique.
* Si ce n'est pas le cas, on rajoute '_' + this.id.
* Malheureusement, dans beforeCreate() l'id n'est pas encore connu.
* Dans afterCreate() on ne peut plus mettre à jour l'objet, donc c'est dans
* la méthode d'instance url() qu'on en fait la mise à jour.
*
* C'est une rustine.
*/
beforeCreate: function(attrs, next) {
// Au début, la qté d'échantillons est la même
// que la qté initiale.
attrs.qte_echantillons = attrs.qte_echantillons_ini;
next();
},
afterCreate: function(attrs, next) {
next();
/*
// On va remplir le champ slug, qui doit être unique
var slug = attrs.titre_annonce.replace(/\s+/g, '-').toLowerCase();
sails.log.verbose('Après la création d\'annonce : ' + attrs.id);
sails.log.verbose('Le slug vaut : ' + slug);
// On va donc rechercher une annonce qui comporte
// déjà un tel slug.
// Si on ne trouve pas, on pourra faire
// attrs.slug = slug;
// Si on trouve quelque chose, alors il faudra allouer
// attrs.slug = slug + '_' + attrs.id (ce qui est le seul
// moyen facile pour obtenir une chaîne unique).
Annonce.findOne({slug: slug}, function(err1, ann) {
if (err1) {
sails.log.error(err1);
next(err1);
}
else if (!ann) {
// Chouette, c'est la première annonce avec ce titre.
sails.log.verbose('C\'est la première annonce de ce nom.');
Annonce.update({id:attrs.id}, {slug:slug}).exec(function(err2, object){
sails.log.error('Infinite loop ? (1)');
if (err2) next(err2);
else {
sails.log.error('Infinite loop ? (3)');
next();
}
});
//attrs.slug = slug;
//next();
} else {
// Malheur, on a trouvé quelque chose, donc ce
// slug n'est pas unique.
sails.log.verbose('Il existe une autre annonce avec ce titre.');
Annonce.update({id:attrs.id}, {slug: slug + '_' + attrs.id}).exec(function(err4, object){
sails.log.error('Infinite loop ? (2)');
if (err4) next(err4);
else next();
});
}
});
*/
},
beforeUpdate: function (attrs, next) {
// On ne met pas à jour le slug, qui doit vivre
// pendant toute la vie de l'annonce.
next();
}
};
Annonce.update()
methodYou shall see below the code I currently have to update the object inside the afterCreate() section. Note that this doesn't work. The request hangs and I see no Infinite loop ? (*)
. After checking, I see that the process comes into the // We didn't find any, so .slug is simple
line.
Know what? I guess that Model.afterCreate()
it is even too late or too early to update the object.
To summerize, my problem is actually simple: I wanted to have a unique slug, and adding the Annonce.id
to it was a good way to manage to. This slug is used to generate an url with an instance method named ann.url()
(see this discussion).
Now, if I don't misunderstand, in beforeCreate()
there is no this.id
nor attrs.id
inside. This is why I need to make it after pushing the object in database.
I wouldn't like to add a random hash/number to the slug because it would generate an ugly url, and it wouldn't ensure me to get a unique slug (note I could enter into a recursive searching and that could be OK).
Maybe afterCreate()
is the wrong place to do this, but the documentation doesn't clearly secify it (once again I am not alone with that true issue due to the implementation or the lack in documentation).
I have thought of updating the slug inside my ann.url()
method. This obviously works. But I will choose to use this workaround if I cannot do anything else: this method is called everywhere inside my templates, so it's part of the 20% code that is most often used (this project needs relatively high performance). So I don't want to add one more conditional block in this method.
afterCreate: function(attrs, next) {
// slug must be unique
var slug = attrs.titre_annonce.replace(/\s+/g, '-').toLowerCase();
// Let's search for a classified that would have such a slug
// If we don't find, we can do
// this.slug = slug;
// If we find sthg, we must get a unique string.
// this.slug = slug + '_' + this.id
// This would fit.
// findOne() <--- because we only want to see if there's sthg
Annonce.findOne({slug: slug}, function(err1, ann) {
if (err1) {
sails.log.error(err1);
next(err1);
}
else if (!ann) {
// We didn't find any, so .slug is simple
Annonce.update({id:attrs.id}, {slug: slug}).exec(function(err2, object){
sails.log.error('Infinite loop ? (1)');
if (err2) next(err2);
else {
sails.log.error('Infinite loop ? (3)');
next();
}
});
} else {
// We found sthg, so slug is not unique.
Annonce.update({id:attrs.id}, {slug: slug + '_' + attrs.id}).exec(function(err3, object){
sails.log.error('Infinite loop ? (2)');
if (err3) {
sails.log.error('SAVE ERROR');
next(err3);
} else next()
});
}
});
},
Annonce.find()
methodI have updated my Model.afterCreate() to make fetch the newly created record in database.
afterCreate: function(attrs, next) {
// slug must be unique
var slug = attrs.titre_annonce.replace(/\s+/g, '-').toLowerCase();
// Let's search for a classified that would have such a slug
// If we don't find, we can do
// this.slug = slug;
// If we find sthg, we must get a unique string.
// this.slug = slug + '_' + this.id
// This would fit.
// findOne() <--- because we only want to see if there's sthg
Annonce.findOne({slug: slug}, function(err, ann) {
if (err) {
sails.log.error(err);
next(err);
}
else if (!ann) {
// We didn't find any, so .slug is simple
Annonce.findOne({id:attrs.id}, function(error, object){
sails.log.error('Infinite loop ? (1)');
if (error) next(error);
else {
sails.log.error('Infinite loop ? (3)');
object.slug = slug;
object.save(function (err) {
sails.log.error('Infinite loop ? (4)');
next();
});
}
});
} else {
// We found sthg, so slug is not unique.
Annonce.findOne({id:attrs.id}, function(error, object){
sails.log.error('Infinite loop ? (2)');
if (error) {
sails.log.error('SAVE ERROR');
next(error);
} else {
object.slug = slug + '_' + attrs.id;
sails.log.error('SAVE OK');
object.save(function (err) {
next();
});
}
});
}
});
},
I get this:
error: Infinite loop ? (1)
error: Infinite loop ? (3)
I don't get any SAVE ERROR
nor SAVE OK
and the request stucks, I think because we don't enter into save()
and no next()
is called.
So what's the problem?
After creating a record in one of my entities, I have to add a new field. I have to do it after because I need the record's ID.
I need to save this and I don't know how to do.
afterCreate: function(obj, next) {
// slug field must be unique
var slug = obj.titre_annonce.replace(/\s+/g, '-').toLowerCase();
// Let's search for a classified that would have such a slug
// If we don't find, we can do
// this.slug = slug;
// If we find sthg, we must get a unique string.
// this.slug = slug + '_' + this.id
// This would fit.
// findOne() <--- because we only want to see if there's sthg
Annonce.findOne({slug: slug}, function(err, ann) {
if (err) {
sails.log.error(err);
next(err);
}
else if (!ann) {
// We didn't find any, so .slug is simple
obj.slug = slug;
obj.save(function(err){
next();
});
} else {
// We found sthg, so slug is not unique.
obj.slug = slug + '_' + obj.id;
obj.save(function (err) {
next();
});
}
});
Error: obj
doesn't have a save()
method. But not using .save()
ends with no database update.
What do you think?
Upvotes: 1
Views: 2255
Reputation: 5979
In your example, "obj" would not have the save method attached. You would need to re-instantiate the object if you want the save method. Assuming "obj" came from "Model"
example:
afterCreate : function(obj,next){
Model.findOne(obj.id, function(err, objectWithSaveMethod){
.....
}
}
Or instead of obj.save() you could
Model.update({slug:slug}, {id:obj.id}, function(err, object){
if(err) next(err);
next();
})
Upvotes: 2