Is it possible to create a validation rule in Kohana 3 that will validate the has_many through relationship?
Using the example on the guide page, a blog post can have many categories through the categories_posts table. Is there a validation rule that can be setup in the Post model to verify at least one category was added?
I tried the following:
public function rules()
{
return array(
'categories' => array(
array(array($this, 'has'), array('categories'))
)
);
}
because I see that the ORM::has function will return true/false. But I think because 'categories' is a relationship, and not a field, the rule I wrote never gets checked.
Any ideas?
You must save Post before adding has_many relations. You can check Post for categories after saving, and mark it as draft if they were not set.
Woo, good idea.
Focus in MVC design pattern. I think that's C business not the M.
if ($post->categories->find_all())
{
//blablabla
}
Since categories is external to the posts table, you'll want to use external validation. Create a function called Model_Post::rule_has_valid_categories($submitted_categories, $post) that returns a boolean denoting whether or not the submitted categories are valid for this post.
Then, create the extra rule just before you try to save the post:
$extra_rules = Validation::factory(array('categories' => $submitted_categories))
->rule(
'categories',
'Model_Post::rule_has_valid_categories',
array(':value', ':model')
);
try
{
$post->save($extra_rules);
}
catch (ORM_Validation_Exception $e)
{
// if categories rule failed, array will contain _external[categories] field
print_r($e->errors('models'));
}
You store the message in /application/messages/models/post/_external.php:
return array(
'categories' => array(
'Model_Post::rule_has_valid_categories' => 'Invalid categories'
),
);
Related
I'm working with typeorm and using repository to load entities. I want to find property and its active tenants (is_active = true)
In PropertyTenant model, I have:
#ManyToOne(type => Property)
#JoinColumn({ name: 'property_id', referencedColumnName: 'id' })
property: Property;
And in property model, I have:
#OneToMany(
() => PropertyTenant, (propertyTenant: PropertyTenant) => propertyTenant.property
)
propertyTenants: PropertyTenant[];
I tried something like this:
return await propertyRepository.findOne({
where: {
id: id,
'propertyTenants.is_active': true
},
relations: [
'location', 'location.city', 'location.city.state', 'propertyPurpose', 'propertyAmenities', 'propertyAmenities.amenity',
'propertyTenants', 'propertyTenants.tenant', 'propertyType', 'propertyDocuments', 'propertyDocuments.documentType', 'propertyImages'
]
});
It gives the error:
No entity column \"propertyTenants.is_active\" was found.
I'm not able to find anything in documentation. I don't want to use query build as it returns raw data and I need to process it.
Is there a way in which I can put condition in #OneToMany or #ManyToOne?
How to solve this?
The answer is to use QueryBuilder. You state you don't want to use it because it returns raw results, but only functions like QueryBuilder.execute() will return raw results. .getOne() and .getMany() will return results in entity format, just like you would get with Repository.findOne().
I've been kinda confused by the relationships as I'm used to save relationship by id, while docs and examples I found suggest to get the entire object and use that instead (Isn't this strange???)
I found this on github addressing this issue ( https://github.com/typeorm/typeorm/issues/447 ) , where they suggest to use an object with just the id property, but it's from 2017. Is that a good way to do it ? And is it still the only way to do it ? (I find it pretty lame tbh)
async create( #Body() product: Product) {
product.category = <any>{ id: product.category };
return { payload: await this.repository.persist(product) };
}
Another one suggested to name the column as categoryId and it would work as expected (with id instead of object) but WHY? What does the name have to do with that ??
#Entity()
class Product {
#Column({ type: "int", nullable: true })
categoryId: number;
#ManyToOne(type => Category)
#JoinColumn({ name: "categoryId" })
category: Category;
}
I'm just confused, help ^_^
Isn't this strange???
Depends how you think about it, but yeah, I also like being able to just set the id, and not fetch the entire related entity.
Is that a good way to do it ? And is it still the only way to do it ?
I am also in the process of figuring out typeorm. What I found out is that you can do:
product.category = <any>3;
// or
product['category' as any] = 3;
repository.save(product) // I don't know how you have the persist() method.
and, in your case, the product.categoryId column will be set to 3. If categoryId is a foreign key and you set a non-existing id, you will get a foreign key error, like you should.
But this way ts will still think that product.category is of type Category. You can also specify the category property as a Category | number. But then you would have to do type checks everywhere which is annoying. I've tested this a bit, but I'm not sure if this will cause some unsuspecting bugs.
What does the name have to do with that ??
Well the option you provided is to define 2 properties: category which is a relation, and categoryId which is the column. The property categoryId should be named like the column in the table, but you can also pass the name: 'actual_name' in the #Column decorator. I don't know what happens if you set both columnId and the column properties with different ids.
According to this GitHub thread, it seems you can also do something like this:
product.category = { id: 1 }
product.save()
// Or
product.category = new Category().id = 1
product.save()
In a situation similar to this one, Getting joined data from strongloop/loopback, where one has Products and product Categories, how does one return the Category Name rather than the id (foreign key) as the default response for /Products? I've been able to hide the id field but not return the name. Thanks.
Supposing you have the relation Product hasOne Category, called productCat
With Node API
Product.find({
include: {
relation: 'productCat', // include the Category object
scope: { // further filter the Category object
fields: 'name', // only show category name
}
}
}, function(err, results) { /* ... */});
With REST API
GET api/Products?filter={"include":{"relation":"productCat","scope":{"fields":"name"}}}
Hope this helps (haven't tested it but it should work)
I have the following situation in Yii2:
Project model
CustomField, defining a custom field type and whether it should be applied to Projects (other options are employees and companies)
CustomFieldContent, related to both an entity (project in this case) and a custom field type
So, an example:
Project with id 1
CustomField with id 2
CustomFieldContent with entityId = 1, type = 'project', customFieldId = 2 and value = 'test'
Now, displaying custom content for each project in Yii's gridview is no problem. But, I want to make it searchable and sortable. Therefore, I need to add the custom field name as an attribute to ProjectSearch. That, however, can't be done as Yii doesn't allow for dynamic attributes.
Any ideas as to how to go about this?
For searchable and sortable content i suggest you this tutorial where you can find useful sample for build what you need. ( the scenario nuber 2 is the more appropriate to your needs)
In short term, you should extend your base model adding the relation you need, setup in searchModel proper functions adding to the dataProvider->setSort for the field/relation and add the where condition for filtering.
below a short extract
$dataProvider->setSort([
'attributes' => [
....
'yourRelatedField' => [
'asc' => ['field1' => SORT_ASC, ],
'desc' => ['field1' => SORT_DESC,],
'label' => 'your Laber',
'default' => SORT_ASC
],
]
]);
and extending the where condition for filtering.
/* Add your filtering criteria */
// filter CustomFieldContent
$query->joinWith(['table_a' => function ($q) {
$q->where('table_a.CustomFieldContent LIKE "%' . $this->CustomFieldContent . '%" ');
}]);
I have Students and Classes with a hasMany association between them.
I do this:
myStudent.setClasses(ids). then(function(result) {
console.log(myStudent.Classes);
});
Questions:
What does result parameter mean inside the then-handler?
Why isn't myStudent.Classes up-to-date with the setClasses() change I made?
How can I have Sequelize update the simple Classes member? I need to return a simple JSON response to the caller.
According to docs, result would be the associated Classes (in you case) when sending them to the .setClasses method.
Therefore, your ids param should be in fact the Classes, perhaps you should require them before
Class.findAll({where: {id: ids}})
.on('success', function (classes) {
myStudent.setClasses(classes)
.on('success', function (newAssociations) {
// here now you will have the new classes you introduced into myStudent
// you say you need to return a JSON response, maybe you could send this new associations
})
})
It's not updating because the queries regarding the associations of objects doesn't rely on you original object (myStudent). You should add the new associations (result var, in your example, newAssociations, in mine) in your existing myStudent.Classes array. Maybe reloading your instance should work as well.
Class.findAll({where: {id: ids}})
.on('success', function (classes) {
myStudent.setClasses(classes)
.on('success', function (newAssociations) {
myStudent.Classes = myStudent.Classes || [];
myStudent.Classes.push(newAssociations);
// guessing you're not using that myStudent obj anymore
res.send(myStudent);
})
})
I hope I answered this one with the previous two answers, if not, could you explain what you mean by updating the Classes member?