I have a bunch of DNS records in a table, where the "type" column defines the type of the given record.
I would like to have a class hierarchy of models in Lithium representing these records such as:
RR - base resource record (abstract - sort of)
RR_SOA - SOA record, extends RR
RR_CNAME - CNAME record, extends RR
... etc ...
So one class for each record, all mapping to the same table.
Next I would like to use RR::find which would automagically give me a list of objects where the class of each object corresponds to the type of the actual record.
Is this even possible without too much black magic?
Yes (anything is possible) but you're approach may be too ambitious. In my POV, I would depend on #class RR to handle the basic querying of the target table, but by utilizing filters on RR (specifically on the find method inherited from Model), you can pass-by-reference the instance to sets of newly instantiated classes (SOA, CNAME, etc...) along with their position in the table (so SOA object is only associated with that specific record or primary key).
There is some black magic going on, but nothing the Lithium core developers didn't account for.
Bottom line, 1 base class for your table (RR model), multiple (possibly inherited from another base unrelated to Model) for SOA, CNAME, etc..., and a filter (put anywhere really) to intervene RR::find and/or the late binded RR::findby*
Does this make sense?
(This will require some trial and error. First to see if you can manipulate the data output, after that the rest is butter)
Related
I have a Entity in my database (say Member) which has many relationships with other tables (6 relationships to be exact). Some of them I don't want mapped with the ORM (I mean linked to this Entity) because they may have many records (like MemberAccessLogs for example) and some other load many other entities.
Now I want this Member Entity to have an isDeletable method so I can disable exclude button in administration page.
If I where to do this the traditional way, I would have to declare the associations with all the other tables in the entity class, including MemberAccessLogs and I would put the method in it so I could test if these associations are empty.
But AFAIU, I would have to make a fetch (or at least a count) to the association's tables in order check for empty.
Another way would be to fetch the Members I want shown and then make a separate query to check for empty with a low cost exists(select * from table limit 1) in these sub-tables and then populate the isDeletable method in Member programmatically before pass it to Twig.
But I found this solution cumbersome. Anyone has a better way to do this ?
Just for the record: Some people may think this is "premature optimization". I maintain (contrary to some), that you should think ahead when you are programming and don't this this is bad. But I really think this isn't the place to discuss it. Please let's focus on the question asked ok ? :)
Edit
To easily prove that limit 1 is increadibly faster than count, I did a small test in a table in my database that has more than 20 million lines. Here are the results:
select count(*) from loga [20 million+ table]
20678473
1 row(s) fetched - 27023ms
select exists(select null from loga limit 1)
true
1 row(s) fetched - 2ms
I guess 13511,5 times faster is conclusive enough. :D
Extra lazy
You could look into extra-lazy associations.
Basically you map all associations as you normally would, and add fetch="EXTRA_LAZY":
/**
* #Entity
*/
class CmsGroup
{
/**
* #ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
Now Doctrine will not load the complete collection into memory the first time it's accessed, but performs specialized queries to load the parts you actually need at that moment.
So $users->count() (or count($users)) on the collection would trigger a simple count-query in stead of loading the complete collection into memory.
PostLoad
You could use an postLoad event to determine if such an entity is deletable. This postLoad event is called after an entity is constructed by the EntityManager, so when the entity is loaded.
Add an unmapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not.
Create an entity listener that listens to the postLoad event. The listener can have the EntityManager, DBAL Connection, or anything else injected. With that dependency you could perform whatever query you want and use the result to set $isDeletable.
The result is a single additional query when the entity is loaded, after which the entity "knows" whether it's deletable or not.
An example of using the postLoad event can found in a Cookbook entry on the Strategy Pattern
Do note that when the conditions that determine whether it's deletable or not change, the value of $isDeletable could become incorrect. To resolve this issue, you could keep track of those conditions:
Keep track
Add a mapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not. It would probably start with true.
When something is added to an association which would mean that the entity is no longer deletable, set $isDeletable to false.
When something is removed from an association that which would mean that the entity is deletable again, set $isDeletable to true.
In other words: with every change you keep track of whether the entity is deletable or not.
This way you won't need any additional queries at all.
There's a Cookbook entry on aggregate fields that explains this concept very well.
Lets say that i have an aggregate root named User the user can have multiple Vehicle, the Vehicle entity has types such as (suv, truck, sedan, etc ...).
In that case if i want to get the User vehicles of type sedan only do i have to design my aggregate to get the sedans directly from the aggregate like User->getSedans() ?
Or can i get that throw the UserRepository->getVehiclesOfTypeSedan() and the aggregates only hold the Vehicles ?
To keep your interface and inheritance hand in hand, I think it is better to separate your logic:
UserRepository->getVehicles("sedan");
Or you can utilize method-chaining:
UserRepository->getVehicles()->sedan;
In the above code, getVehicles(), returns a collection object which has NOT a property named sedan.
You have to use php __get() to do the operation.
Why then I do this? Because if you were able to fetch the cars by type though a property (remember properties are not functions, there is no execution), then it literally means that you have fetches all types and have them ready:
WRONG WAY AND MEMORY-KILLER APPROACH is that you fetch all of the user's car and put each type in its associated property.
Better way is returning an object, the object then is shipped with a function __get() to be invoked on each property retrieval:
$object->sedan;
sedan does not exist, so you have already predicted that:
function __get($param)
{
$this->__getByType($param);
}
Though I prefer my very first suggestion.
Recently started working with OOP in PHP. Following the "code to an Interface" principle, i got confused as to the type hint to use when passing a single object or multiple as argument to a method.
Currently, i have a "Student" class - represents a row in my students table, i also have a "Students" class that holds multiple student objects in an array.
To fetch the profile of one student, i pass the Students object (holding a single student object) to the profile class. I set a Students type hint in the profile class.
Now i feel this is bad code as i have lines like this
student = new Students();
and students = new Students();
question is,
am i on the right path?
if i remove the Students class and work with Student alone, based on the principle, how do i pass multiple Student objects (assuming array) to the profile class if it accepts a Student type hint?
what options do i have?
Thanks.
If by Students you mean a collection of Student objects, perhaps a better name would be StudentCollection or StudentSet.
There are two ways around the type hint problem:
Introduce a method on StudentCollection called ->getProfiles(); it would return an array of profiles for each Student instance it's managing by calling methods on Profile.
Introduce a (static) method on Profile that operates on a StudentCollection instance.
The first option has feature envy, which is why I've included a workaround.
Instead of reinventing the wheel you might want to try Doctrine or at least take a look at its architecture.
I'm not sure if I get your exact issue... But if you want to go for your own code I would first abstract the DB layer as well and have some base classes like Database, Table, Row, Field that an describe the DB stack and extend them as needed with some magic methods. So when you do Student extends Table it would automatically check for a "students" table or whatever else convention you like to implement. Alternatively you could just pass the table name as arg.
Whatever Object is returning the result set from the database would have to construct a single Row object for each row and add it to a collection of rows that I would name ResultSet and contains all the row objects and return that collection.
I need to build a family tree in php and MySQL. I'm pretty surprised at the lack of open source customizable html family tree-building software there is out there, but I digress. I have spent a lot of time reading about storing MySQL digraphs and family trees. Everything makes sense to me: have a table with nodes (people) and a table with edges (relationships).
The only problem I have is I'm not sure of the best way to store relationships that are not necessarily adjacent, for example sibling and grandparent relationships. At first I didn't think this would be a big deal because I can just invisibly enforce a parent (everyone has parents) that would resolve these connections.
However, I also need to be able to store relationships that may not have a common parent such as romantic partners. Everything I have read suggests a parent-child relationship, but since romantic partners do not share a common parent (hopefully), I'm not sure how to store it in the edges table. Should I use a different table, or what? If it's in the same table, how do I represent this? As long as I am doing this with non-familiar relationships, I might as well do it with family too.
To sum up, three questions:
How do I represent lateral relationships?
If a lateral relationship has a common parent, how do I store it? Should this be a family flag on the table where other lateral relationships are stored?
How do I store parent-child relationships where the child is two or more edges away (a grandparent), but the immediate parent is unavailable?
Any help is appreciated, and if anyone has any suggestion for some javascript/html family tree building software, that would be wonderful.
An idea that comes from the Geneapro schema and RootsMagic.
person
------
person_id
name (etc)
life_event_types
----------------
life_event_type_id
life_event_type_description (divorce, marriage, birth, death)
life_events
-----------
life_event_id
life_event_type_id
life_event_description
life_event_date
life_event_roles
----------------
life_event_role_id
life_event_role (mother, father, child)
person_event_role
-----------------
person_id - who
life_event_id - what happened
life_event_role_id - what this person did
So you could have a life event of type "birth", and the role_id tells you who were the parents, and who was the child. This can be extended to marriages, deaths, divorces, foster parents, surrogate parents (where you might have 3 or 4 parents with a very complicated relationship), etc.
As for storing more distant relationships, you can calculate these. For example, you can calculate the Father of anybody by getting the person who has the 'father' role with a matching event_id. You can then get the father of that person, and you have the grandfather of the original person. Anywhere that somebody is unknown, create the person with unknown data.
person
-------
person_id
other_stuff
relation
----------
person_id_1
person_id_2
relationship_type
begin_dt
end_dt
just populate the relationship type with any value you are interested in. (FK to some picklist would be great)
I put the dates on for an interesting subdiscussion/thought provokation.
The GEDCOM data model and the Gramps data model are two of the most popular formats for exchanging geneological data between different tools. Using either of these data models should both (1) make your tool more compatible with other tools and (2) ensure that your data model is compabible with many special cases, considering both data models are specially designed to deal with geneological data.
Tools like Oxy-Gen or the Gramps PHP exporter should get you on your way with respect to how to import GEDCOM data into a database.
For more details, see also my answer to “Family Tree” Data Structure.
Consider, Base class A and derived class B.
Both interact with database. Class A uses table A and Class B (which is derived from A) also uses table B.
If I create an object of Class B and later at some stage in some file I access the same object by Class A and delete it then it deletes its instance in Table A but a part of its data is left in Table B (which is left with no parent). How to tackle this problem without defining Class A as abstract, as it has its own existence?
Thanks!
Sanket
Your best approach would probably be to utilise your database backend's foreign key support if it has it. Assuming table A represents an object of class A, and table B holds the additional state that an object of class B needs, then that means that all A and B objects have entries in the A table, but only B objects have entries in both the A and B tables.
If an entry in the A table and an entry in the B table relate to state for the same B object, then you should define a foreign key in the B table that references the ID of the corresponding row in the A table. Now if you attempt to delete a row from A, the database will know that the row in A you're deleting has an associated row in B and can act accordingly. It can either prevent the deletion of the data in A, also delete the corresponding row in B, or change the B row's foreign key value to reference some default value. What precise action you take will depend on what you're trying to achieve.
If your database does not support foreign keys (MySQL when working with the myISAM engine, for example) then things become a lot more complicated. There are possibly ways of doing it in your PHP code, but all the ones I can think of off the top of my head require your base class to know details about the implementation of your derived classes, which is a huge no no in object oriented design.
You should also be asking yourself, why are you initializing a base class object with data that represents a derived class object? Is this actually necessary for your design? I've personally not run across a situation where this has been necessary, because a derived class implicitly always has all the functionality of the base class it derives from.
I searched and finally found a workaround for this using "get_called_class()" PHP 5 function.
And in my views, this is the best way to do it.
In the Base class table, add a "class_type" column and while adding data add the the name of the class which added the row by "get_called_class()". Now, whenever any derived class calls this, the name of the class is added to the database.
When deleting the rows of DB, allow only that class to delete which created that row (by matching "class_type" and "get_called_class()")