Loading class attributes in Yii2 - yii2

For example I have the following classes:
class A extends yii\base\Model {
public $attr1;
public $attr2;
}
class B extends yii\db\ActiveRecord {
... some attributes in table including attr1 and attr2 ...
}
Is it more elegant way to load() values from object B to object A than
$objectA = new A();
$objectB = new B();
... obtaining values for $objectB ...
$objectA->load([StringHelper::basename($objectB->className()) => $objectB->attributes])
?

You can do a
$objectA->attributes = $objectB->attributes;

Related

Using #JsonIgnore selectively on parent class fields

I have a Class A which is extended by multiple classes, say Class B, Class C and Class D.
However I want only Class D to ignore super class fields during serialization.
How do I implement this? If I use #JsonIgnore annotation on parent Class A, all child classes get impacted.
I can see 2 ways:
1 - You can use a JacksonAnnotationIntrospector to dynamically ignore fields, here we test if the field is from class A (see example below when serialising class C)
class CustomIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return m.getDeclaringClass() == A.class;
}
}
2 - You can use the #JsonIgnoreProperties annotation to ignore the fields you don't want (see example below on the definition of class D)
Then with the following class
class A {
public String fieldA = "a";
}
class B extends A {
public String fieldB = "b";
}
class C extends A {
public String fieldC = "c";
}
#JsonIgnoreProperties(value = { "fieldA" })
class D extends A {
public String fieldD = "d";
}
Then use the ObjectMapper
public static void main(String[] args) throws Exception {
A a = new A();
String jsonA = new ObjectMapper().writeValueAsString(a);
System.out.println(jsonA);
// No filtering, will output all fields
B b = new B();
String jsonB = new ObjectMapper().writeValueAsString(b);
System.out.println(jsonB);
// Using the CustomIntrospector to filter out fields from class A
C c = new C();
ObjectMapper mapper = new ObjectMapper().setAnnotationIntrospector(new CustomIntrospector());
String jsonC = mapper.writeValueAsString(c);
System.out.println(jsonC);
// Using #JsonIgnoreProperties to filter out fields from class A
D d = new D();
String jsonD = new ObjectMapper().writeValueAsString(d);
System.out.println(jsonD);
}
outputs
{"fieldA":"a"}
{"fieldA":"a","fieldB":"b"}
{"fieldC":"c"}
{"fieldD":"d"}

Representing Laravel model when the id of table A can be set in columns foo and bar of table B

My model has table A and table B.
The table B contains two columns: foo and bar.
Both foo and bar contain a different id of table A.
How should it be represented in the model ?
I think you can make 2 haveMany relationships from Model A to Model B.
for Model A:
public function foo(){
return $this->hasMany('App\ModelB','foo','id');
}
public function bar(){
return $this->hasMany('App\ModelB','bar','id');
}
And ModelB will have to belongsTo relationships.
public function foo {
return $this->belongsTo('App\ModelA','foo','id');
}
public function bar {
return $this->belongsTo('App\ModelA','bar','id');
}
You can define the relationships by providing the keys as parameters to the eloquent methods.
https://laravel.com/docs/5.5/eloquent-relationships#one-to-one
Something like this:
<?php
class TableB extends Model{
// Get TableA from the TableB foo column
public function tableAFromFoo(){
$this->hasOne(TableA::class, 'id', 'foo');
}
// Get TableA from the TableB foo column
public function tableAFromBar(){
$this->hasOne(TableB::class, 'id', 'bar');
}
}
class TableA extends Model{
// Inverse of the relationships
public function tableBUsingFoo(){
$this->belongsTo(TableB::class, 'foo', 'id');
}
public function tableBUsingBar(){
$this->belongsTo(TableB::class, 'bar', 'id');
}
}
?>

Find records with Many2Many using activeJDBC

I have these annotations for the models:
#Many2Many(other = Course.class, join = "registrations", sourceFKName = "student_uid", targetFKName = "course_uid")
public class Student extends Model {
}
#Many2Many(other = Student.class, join = "registrations", sourceFKName = "course_uid", targetFKName = "student_uid")
public class Course extends Model {
}
How do I get all Students belong to a course UID?
First, you do not need to specify the same annotation twice. This will work just the same:
public class Student extends Model {}
#Many2Many(other = Student.class, join = "registrations", sourceFKName = "course_uid", targetFKName = "student_uid")
public class Course extends Model { }
Second, your case is described on this page: http://javalite.io/many_to_many_associations#select-related-objects
so, you would:
Course course = Course.findById(id);
List<Student> students = course.getAll(Student.class);
That is all!

Dynamically Changing Table Names

I am currently working with a database that has read and write tables.
There are always two tables with the same schema, distinguished by a number as suffix, eg. table1 and table2.
Now, there is another source where I get the current number from. I have to use this number to select from the corresponding table with the matching suffix.
Right now, for every table i have a #MappedSuperclass containing the schema and two implementation classes specifying the table name via #Table(name = "..1") and #Table(name = "..2").
This solution works but by now I discovered a lot of drawbacks and fear there will be many more. Is there another, better way to solve this?
Unfortunately, I could not find out what this kind of database mechanism is called hence I could not find any other sources on the internet.
Thank you in advance!
The most obvious solution:
if ( num == 1 )
{
Table1 table1 = createTable1();
table1.set...;
entityManager.persist( table1 );
} else
{
Table2 table2 = createTable2();
table2.set...;
entityManager.persist( table2 );
}
Or with constructor calling by name (with Lombok annotations):
#Entity
#Data
public class CommonBase
{}
#Entity
#Data
public class Table1 extends CommonBase
{}
#Entity
#Data
public class Table2 extends CommonBase
{}
#Stateless
#LocalBean
public class CommonBaseBean
{
#Inject
private CommonBaseBUS commonBaseBUS;
protected void clientCode()
{
Table0 t0 = (Table0) commonBaseBUS.createEntityByIndex( 0 );
t0.set...();
commonBaseBUS.persisEntity( t0 );
Table1 t1 = (Table1) commonBaseBUS.createEntityByIndex( 1 );
t1.set...();
commonBaseBUS.persisEntity( t1 );
}
}
#Dependent
class CommonBaseBUS
{
#Inject
private CommonBaseDAL commonBaseDAL;
#Setter
private String entityBaseName = "qualified.path.Table";
public CommonBase createEntityByIndex( int index_ ) throws ClassNotFoundException
{
String entityName = entityBaseName + Integer.toString( index_ );
return createEntityByName( entityName );
}
public void persisEntity( CommonBase cb_ )
{
commonBaseDAL.persistEntity( cb_ );
}
protected CommonBase createEntityByName( String entityName_ ) throws ClassNotFoundException
{
Class<?> c = Class.forName( entityName_ );
try
{
return (CommonBase) c.newInstance();
}
catch ( InstantiationException | IllegalAccessException ex )
{
throw new ClassNotFoundException();
}
}
}
#Dependent
class CommonBaseDAL
{
#PersistentContext
private EntityManager em;
public void persisEntity( CommonBase cb_ )
{
em.persistEntity( cb_ );
}
}

How to add an extra property into a serialized JSON string using json.net?

I am using Json.net in my MVC 4 program.
I have an object item of class Item.
I did:
string j = JsonConvert.SerializeObject(item);
Now I want to add an extra property, like "feeClass" : "A" into j.
How can I use Json.net to achieve this?
You have a few options.
The easiest way, as #Manvik suggested, is simply to add another property to your class and set its value prior to serializing.
If you don't want to do that, the next easiest way is to load your object into a JObject, append the new property value, then write out the JSON from there. Here is a simple example:
class Item
{
public int ID { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Item item = new Item { ID = 1234, Name = "FooBar" };
JObject jo = JObject.FromObject(item);
jo.Add("feeClass", "A");
string json = jo.ToString();
Console.WriteLine(json);
}
}
Here is the output of the above:
{
"ID": 1234,
"Name": "FooBar",
"feeClass": "A"
}
Another possibility is to create a custom JsonConverter for your Item class and use that during serialization. A JsonConverter allows you to have complete control over what gets written during the serialization process for a particular class. You can add properties, suppress properties, or even write out a different structure if you want. For this particular situation, I think it is probably overkill, but it is another option.
Following is the cleanest way I could implement this
dynamic obj = JsonConvert.DeserializeObject(jsonstring);
obj.NewProperty = "value";
var payload = JsonConvert.SerializeObject(obj);
You could use ExpandoObject.
Deserialize to that, add your property, and serialize back.
Pseudocode:
Expando obj = JsonConvert.Deserializeobject<Expando>(jsonstring);
obj.AddeProp = "somevalue";
string addedPropString = JsonConvert.Serializeobject(obj);
I think the most efficient way to serialize a property that doesn't exist in the type is to use a custom contract resolver. This avoids littering your class with the property you don't want, and also avoids the performance hit of the extra serialization round trip that most of the other options on this page incur.
public class SpecialItemContractResolver : DefaultContractResolver {
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
var list = base.CreateProperties(type, memberSerialization);
if (type.Equals(typeof(Item))) {
var feeClassProperty = CreateFeeClassProperty();
list.Add(feeClassProperty);
}
return list;
}
private JsonProperty CreateFeeClassProperty() {
return new JsonProperty {
PropertyName = "feeClass",
PropertyType = typeof(string),
DeclaringType = typeof(Item),
ValueProvider = new FeeClassValueProvider(),
AttributeProvider = null,
Readable = true,
Writable = false,
ShouldSerialize = _ => true
};
}
private class FeeClassValueProvider : IValueProvider {
public object GetValue(object target) => "A";
public void SetValue(object target, object value) { }
}
}
To use this functionality:
// This could be put in a static readonly place so it's reused
var serializerSettings = new JsonSerializerSettings {
ContractResolver = new SpecialItemContractResolver()
};
// And then to serialize:
var item = new Item();
var json = JsonConvert.Serialize(item, serializerSettings);