Custom Accessor on Entity Model Spring Kotlin - spring

I've been coding using Laravel and now I want to learn Spring with Kotlin and my first problem is.. How do I make custom accessor in model like I usually do in Laravel?
In Laravel we do:
public function getTypeAttribute($value) {
return strtoupper($value);
}
How do I do this in Spring Kotlin Entity Model? Thanks you!

To define custom accessors on a property, you use following syntax
class TestClass {
var name: String = ""
get(){
// You can reference the backing field with identifier `field` in accessors
// print("Current value of field is : $field")
return getNameFromDb()
}
set(value){
setNameInDB(value)
}
}
// Now if you do the following
val name = TestClass().name // This will call the get() block
TestClass().name = "SomeName" // This will call the set(value) block

Related

Deserialize nested Kotlin object in springMVC

I'm trying to deserialize a complex GET request into a structure of nested objects.
The GET requests looks like:
curl 'localhost:8080/?id=1&inner.id=1'
and the code should look like this:
class RootObj(val id: Int, inner: InnerObject)
class InnerObject(val id: Int)
#RestController
class SearchController {
#GetMapping(path = ["/"])
fun getRoot(rootObj: RootObj): String {
return "ok"
}
}
This doesn't work out of the box I guess because spring doesn't know how properly create this nested structure.
Parameter specified as non-null is null: [...] parameter inner","path":"/"}%
Is there a way to overcome this problem? Maybe providing a totally custom deserializer code?
As alternative solution, I guess I could flatten the object hierarchy but for doing so I must be able to map a query parameter like inner.id to a field named innerId
Spring can actually map the query params to the custom object directly, but you need to provide defaults to the params of the custom object constructor.
So you need to define your classes as below for it to work
class RootObj(val id: Int = 0, val inner: InnerObject = InnerObject(0))
class InnerObject(var id: Int = 0)
Do note that the id field of InnerObject would have to be declared as var for Spring to be able to map it. Then curl 'localhost:8080/?id=1&inner.id=1' would work fine.

Laravel Accessor on Pascal Case Attributes

I am getting data from an external db which uses PascalCase for column names eg: ClientStatus,
I did write an accessor, but due to casing issue its not working.
public function getClientStatusAttribute() {
return $this->attributes['ClientStatus']."modified value";
}
You can either access it using ClientModel->client_status or override HasAttribute trait's two methods hasGetMutator() and mutateAttribute()
Laravel Model uses HasAttribute trait.
Model uses HasAttribute trait's two methods hasGetMutator() and mutateAttribute() to check if accessor exits or not and if exsits then return its value.
hasGetMutator() which by default uses studly helper method to make snake_case attribute to PascalCase
That's why we create accessor get{AttributeName}Attribute in this manner.So,make these methods something link this
public function hasGetMutator($key)
{
return method_exists($this, 'get'.$key.'Attribute');
}
protected function mutateAttribute($key, $value)
{
return $this->{'get'.$key.'Attribute'}($value);
}
Above we removed studly helper method, so when we try access a PascalCase attribute, first it will check if accessor exists, if yes then return its value.

kotlin var property in tornadofx ViewModel and ItemViewModel

As per docs, kotlin var properties can be bind in ViewModel as
// Kotlin var property
class PersonVarViewModel(person: Person) : ViewModel() {
val name = bind { person.observable(Person::name) }
}
It seems like doesn't work.
How to solve this issue. IDE shows red underline bellow "bind"
but if i write
val name = bind(RoomType::name)
it shows no error. but updating the value using UI fields does'nt update the model value.
Please help
In your class declaration, use var person: Person.
person needs to be a member of the class, not just a parameter to the constructor. You can do this by declaring it var or val in the constructor parameters, or you can add a member field to the class the conventional way and assign it (probably using by property, but not sure if that's what you want)
class PersonVarViewModel(var person: Person) : ViewModel() {
val name = bind { person.observable(Person::name) }
}
For ItemViewModel ...
class PersonVarViewModel(var person: Person) : ItemViewModel<Person>() {
val name = bind { person.observable(Person::name) }
}
You need to make the ItemViewModel aware of the person instance, but also let it react to changes to the underlying item later. You need to assign the person you pass in to the item property of the ItemViewModel. This can be done by passing it in the constructor:
class PersonVarViewModel(person: Person) : ItemViewModel<Person>(person) {
val name = bind(Person::name)
}
Be aware that if you add this constructor, you can only use the that viewmodel with injection if you push it manually into scopes, since it can't be instantiated by the framework. You should therefore either add a noargs constructor as well, or simply omit the person parameter and assign to item after you create it.
If you update the value in the underlying person, it will only be visible in the view model if the value is observable. If not, you have to call rollback() to update changes from the person. You can call rollback for specific fields only.
If possible, use observable properties in your domain model objects to avoid such issues.

Yii2: How to set default attribute values in ActiveRecord?

This may seem like a trivial question, however all of the obvious solutions that I can think of have their own flaws.
What we want is to be able to set any default ActiveRecord attribute value for new records only, in a way that makes it readable before and during validation and does not interfere with derived classes used for search.
The default values need to be set and ready as soon as we instantiate the class, so that (new MyModel)->attr returns the default attr value.
Here are some of the possibilities and the problems they have:
A) In MyModel override the init() method and assign default value when isNewRecord is true like so:
public function init() {
if ($this->isNewRecord) {
$this->attr = 'defaultValue';
}
parent::init();
}
Problem: Search. Unless we explicitly unset our default attribute in MySearchModel (very error-prone because it is too easy to forget), this will also set the value before calling search() in the derived MySearchModel class and interfere with searching (the attr attribute will already be set so search will be returning incorrect results). In Yii1.1 this was resolved by calling unsetAttributes() before calling search(), however no such method exists in Yii2.
B) In MyModel override the beforeSave() method like so:
public function beforeSave($insert) {
if ($insert) {
$this->attr = 'defaultValue';
}
return parent::beforeSave();
}
Problem: Attribute is not set in unsaved records. (new MyModel)->attr is null. Worse yet, even other validation rules that rely on this value will not be able to access it, because beforeSave() is called after validation.
C) To ensure the value is available during validation we can instead override the beforeValidate() method and set the default values there like so:
public function beforeValidate() {
if ($this->isNewRecord) {
$this->attr = 'defaultValue';
}
return parent::beforeValidate();
}
Problem: Attribute is still not set in unsaved (unvalidated) records. We need to at least call $model->validate() if we want to get the default value.
D) Use DefaultValidator in rules() to set a default attribute value during validation like so:
public function rules() {
return [
[
'attr', 'default',
'value' => 'defaultValue',
'on' => 'insert', // instantiate model with this scenario
],
// ...
];
}
Problem: Same as B) and C). Value is not set until we actually save or validate the record.
So what is the right way to set default attribute values? Is there any other way without the outlined problems?
There's two ways to do this.
$model => new Model();
Now $model has all the default attributes from the database table.
Or in your rules you can use:
[['field_name'], 'default', 'value'=> $defaultValue],
Now $model will always be created with the default values you specified.
You can see a full list of core validators here http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html
This is a hangup with Yii's bloated multi-purpose ActiveRecords
In my humble opinion the form models, active records, and search models would be better off split into separate classes/subclasses
Why not split your search models and form models?
abstract class Creature extends ActiveRecord {
...
}
class CreatureForm extends Creature {
public function init() {
parent::init();
if ($this->isNewRecord) {
$this->number_of_legs = 4;
}
}
}
class CreatureSearch extends Creature {
public function search() {
...
}
}
The benefits of this approach are
You can easily cater for different validation, set up and display cases without resorting to a bunch of ifs and switches
You can still keep common code in the parent class to avoid repetition
You can make changes to each subclass without worrying about how it will affect the other
The individual classes don't need to know about the existence of any of their siblings/children to function correctly
In fact, in our most recent project, we are using search models that don't extend from the related ActiveRecord at all
I know it is answered but I will add my approach.
I have Application and ApplicationSearch models. In Application model I add init with a check of the current instance. If its ApplicationSearch I skip initializations.
public function init()
{
if(!$this instanceof ApplicationSearch)
{
$this->id = hash('sha256', 123);
}
parent::init();
}
also as #mae commented below you can check for existence of search method in current instance, assuming you didn't add any method with name search to the non-search base model so the code becomes:
public function init()
{
// no search method is available in Gii generated Non search class
if(!method_exists($this,'search'))
{
$this->id = hash('sha256', 123);
}
parent::init();
}
I've read your question several times and I think there are some contradictions.
You want the defaults to be readable before and during validation and then you try init() or beforeSave(). So, assuming you just want to set the default values in the model so they can be present during the part of the life cycle as long as possible and not interfere with the derived classes, simply set them after initialising the object.
You can prepare separate method where all defaults are set and call it explicitly.
$model = new Model;
$model->setDefaultValues();
Or you can create static method to create model with all default values set and return the instance of it.
$model = Model::createNew();
Or you can pass default values to constructor.
$model = new Model([
'attribute1' => 'value1',
'attribute2' => 'value2',
]);
This is not much different from setting the attributes directly.
$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';
Everything depends on how much transparent would you like your model be to your controller.
This way attributes are set for the whole life cycle except the direct initialisation and it's not interfering with derived search model.
Just override __construct() method in your model like this:
class MyModel extends \yii\db\ActiveRecord {
function __construct(array $config = [])
{
parent::__construct($config);
$this->attr = 'defaultValue';
}
...
}
If you want to load default value from database you can put this code in your model
public function init()
{
parent::init();
if(!method_exists($this,'search')) //for checking this code is on model search or not
{
$this->loadDefaultValues();
}
}
You can prepare separate method where all defaults are set and call it explicitly.
$model = new Model;
if($model->isNewRecord())
$model->setDefaultValues();

Get model name dynamically in cakephp 2.x

Is there any function to get the all table or model names in cakePhp.
I want to update my table's field and for that I need to select all table dynamically so that in future when I'll add new table I do not have to make changes in function.
The new table automatically update the fields.
I don't quite understand the question - from what i gather you want to write abstract actions to account for multiple models? If this is the case you can add abstract actions into the AppController and use $this->modelClass. This will return the model name from were you are calling the abstract action from. For example if you calling the abstract action from 'UsersController' which by default uses the Model 'User' then the modelClass will return 'User'.
class AppController extends Controller {
public function abstractAdd() {
// Get the model in use
$this->{$this->modelClass}->create();
// Use the save method in that model
if ($this->{$this->modelClass}->save($data)) {
// do something
}
}
}
class UsersController extends AppController {
public function add() {
$this->abstractAdd();
}
}
Hope this helps

Resources