loose coupling example - loose-coupling

intro code:
public interface Course {
/**
* returns the number of units (hours) a specific course is
*/
public int units();
/**
* returns the number of students signed up for the course
*/
public int numOfStudents();
/**
* returns the maximum number of students the course can have signed up to it
*/
public int maxNumStudents();
/**
* returns whether or not the student is enrolled in the course
*/
public boolean registered(Student s);
/**
* enrolls s in the course
*
* #pre this.registered(s) == false
* #pre this.numOfStudents() < this.maxNumStudents()
*
* #post this.registered(s) == true
* #post this.numOfStudents() == $prev(this.numOfStudents()) + 1
*/
public void register(Student s);
}
public interface Student {
/**
* enrolls the student in course c
*
* #pre c.registered(this) == false
* #pre c.numOfStudents() < c.maxNumStudents()
* #pre this.totalUnits() + c.units() <= 10
*
* #post c.registered(s) == true
* #post this.totalUnits() == $prev(this.totalUnits()) + c.units()
*/
public void register(Course c);
/**
* return the total number of units this student currently is enrolled in
*
* #pre true
* #post 0 <= $ret <= 10
*/
public int totalUnits();
}
in the example code im trying to describe two separate entities (interfaces/classes/whatever) which on the one hand should be (i would like, at least) loosely coupled but on the other hand do depend on each other and require a certain knowledge of each other.
in the above scenario i would need a third class that actually unites them into a working system. its ugly because as of now, the above definition is as loosely coupled as can be - student.register(c) only changes the student object, and course.register(s) only changes the course object. so a unifying class would have to run both s.register(c) and c.register(s).
although if i relogate all the register() logic to one class then i tightly couple them.
is there a cleaner way of designing this?

By using interfaces you are already reducing the level of dependency between concrete objects and that is a good thing. Some dependency is necessary for your system to work so you have to decide how much you want to tolerate.
Consider that a Student in your system can register for a course. Since Course is an interface it may be possible to implement a number of different types of Courses and the student will then be able to register for any of them. As long as the student only knows the Course interface that should be fine. And the same thing for the Course, it only knows the Student interface not the concrete Students.
Just one thing. In a situation where you have a bi-directional association as you describe I like to make one side the owner of the relationship. That is, I can decide that Student owns the relationship and as such, a Student may register for the Course but the Course does not Register the Student.
Then all client code will make a single call s.register(c). The register in Student will then take care of the inverse side of the relationship. This reduces the need of client code to know both sides of the relationship.

Related

Symfony Sonata OneToMany, Sum or Total of a field is not showing in the Admin List

I have 2 entities, Client and Campaign.
Client Entity
/**
* #ORM\OneToMany(targetEntity="Campaign", mappedBy="client")
*/
protected $campaign;
+++++++++++++++++++++++++++++
Campaign Entity
/**
* #var integer
*
* #ORM\Column(name="numberOfBid", type="integer", nullable=true)
*/
protected $numberOfBid;
/**
* #ORM\ManyToOne(targetEntity="Clients", inversedBy="campaign")
* #ORM\JoinColumn(name="client_id", referencedColumnName="client_id")
*/
protected $client;
/* Let's say
Client A has Campaign A, numberOfBid = 1
Client A has Campaign B, numberOfBid = 5
Client A has Campaign C, numberOfBid = 3
Client A has Campaign D, numberOfBid = 4
Total numberofBid = 13
*/
Problem: How do I get the sum of all numberOfBid and show it as 1 column in the Client Admin List Board? On the method configureListFields, I tried different ways like using sonata_type_model, doctrine_orm_callback, query but still didn't work.
Client ID | Campaign TotalBid
A | 13
Hoping for your feedback.
Thanks in advance.
First you should rename your
protected $campaign;
in
protected $campaigns;
cause it's a collection. One client has many campaigns.
To your problem: You could implement a method on your client entity something like this
class Client{
...
public function getTotalNumberOfBids() {
$i = 0;
foreach ($this->getCampaigns() as $campaign) {
$i += $campaign->getNumberOfBid();
}
return $i;
}
...
}
and add to your list view by
protected function configureListFields(ListMapper $list)
{
$list
...
->add('totalNumberOfBids');
}
The "magic getter" will automatic invoke the getTotalNumberOfBids method.
Note: Dependent on the number of objects holding by the campaigns collection, the summation of the numberOfBids could be slow due to the use of the foreach loop.
There are some conventions in symfony that can be found here http://symfony.com/doc/2.8/contributing/code/conventions.html ... so if you want to access a property from your entity and you dont get it explicit by calling it with an leading keyword like "get" or "is", symfony (or maybe a underlying framework) automatic tries to find a method starting with "get" or "has" and so on ... this is comfortable because you dont have to write the get, set, has and so on before your properties to call the getting and setting methods.
The tricky thing is, that sonata admin bundle doesn't give you an error in the list view if a property isn't found. For example totalnumberOfBids will left your column empty without any error, cause you dont match the camel-case for the part "number". So you should be aware of naming/calling your properties correct or you will end up in a long time of headache ...

Combining multiple one-to-one relationships in Doctrine on inverse side of relationship

Hoping for some help thinking this through. Say I'm working with two entities here - User and Group. For the purposes of this example, say each Group has a senior moderator and a junior moderator, both of whom are Users and are mutually exclusive (a user can only be in one group, and cannot be both the senior and junior moderator). So in Group, I might have something like:
class Group
{
/**
*
* #OneToOne(targetEntity="User")
*/
private $seniorModerator;
/**
*
* #OneToOne(targetEntity="User")
*/
private $juniorModerator;
}
Now in User, I don't care whether the user is a senior moderator or junior moderator. I just want to be able to return what group a user is in. I know I'll have to add mappedBys in the Group properties, but I'm sort of stumped because I know I can't have two mappedBys assigned to the same thing ($group for example).
Would the best solution here be to just create two separate inverse properties in User (like $group1 and $group2) and then create a getGroup() getter method that just checks whether either of those properties is populated and returns the one that is?
Is there a less hacky method I'm not thinking of? Appreciate any advice, thanks in advance.
I have similar situation so I have created a third table with manyToOne to User, manyToOne to Group and one field that says if this user for this group is manager. So you can do the same add and two fields that states if user is senior or junior.
i think the best solution is to do a "one to one Unidirectional" association in both group and user entities, so the code for your group will be :
class Group
{
private idGroup;
/**
*
* #OneToOne(targetEntity="User")
* #JoinColumn(name="seniorModerator_id", referencedColumnName="idUser")
*/
private $seniorModerator;
/**
*
* #OneToOne(targetEntity="User")
* #JoinColumn(name="juniorModerator_id", referencedColumnName="idUser")
*/
private $juniorModerator;
}
For your User :
class User{
private idUser;
/**
*
* #oneToOne(targetEntity="Group")
* #JoinColumn(name="group_id", referencedColumnName="idGroup")
*/
private $group;
}
if you want more information on Association Mapping hir are the documentation : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-unidirectional

Symfony - access object of non related tables

How can i access the object of second table when joined (non-related tables)?
I have two table which are not related and I want to get the object of the second class (from below dump output)
My repository with dump
For example:
my controller:
$ProductSet_Repo = $em->getRepository('MyTestBundle:Product\ProductSet')->FindProductSet($productid);
Normally when the tables are related I can simple do
$productSet = $ProductSet_Repo->getproductid()->getProduct(); to get the object of Product class From ProductSet Class.
See My Dump
However since the tables are not in relationship and when i dump the data i get the objects of two classes is there a way I can access the Object My\TestBundle:Products\Entity\Product\ProductSet and \My\TestBundle\Entity\Product\Product?
Note: i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes
Also I know I can select the fields which i want to retrieve. (I dont want to do that)
You write:
i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes.
But with doctrine you are very well able to make a association between two entities without changing the tables. As far as I can see from your query you have a product_id column in your product_set table. That is all you need to make an association between Product and ProductSet.
In your ProductSet class you can do:
<?php
namespace My\TestBundle\Entity\Product;
class ProductSet
{
//... other properties
/**
* #var Product
* #ORM\Id
* #ORM\ManyToOne(targetEntity="My\TestBundle\Entity\Product\Product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
/**
* Set the product.
*
* #param Product $product
* #return ProductSet
*/
public function setProduct(Product $product)
{
$this->product = $product;
return $this;
}
/**
* Get the product.
*
* #return Product
*/
public function getProduct()
{
return $this->product;
}
//... other setters and getters
}
Now you can do:
$repository = $em->getRepository('MyTestBundle:Product\ProductSet')
$productSets = $repository->findBy(array('product' => $productid));
foreach($productSets as $productSet){
$productSet->getProduct()->getId() === $productId; // true
}
You can still join them (despite of strange naming convention you have id of corresponding object in the other entity) using query builder or native sql, but it's a really bad way.
it was developed by previous webdeveloper and i dont want to spend more time as i work as free lancer
That's not an excuse. You should create a relation and migration for these data. Getting money for a poorly designed and developed app is not cool.
Probably additional work when working with that poor design will take your more time than doing it in a proper way.

Best way to reference an entity type as a field on another entity in Symfony2 and Doctrine2

In my SF2/Doctrine2 application i have a entity "User", then another abstract parent entity (which I'll call plant for simplicity's sake) with two subtypes (tree and bush). What I want is for the user to be able to configure which plant subtype they are going to use, then any time they create a new Plant it creates one of this subtype, tree or bush.
My discriminator column looks like this on plant:
/**
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* 0 = "Tree",
* 1 = "Bush"
* })
*/
My question is how best to create a field on the user entity to select the entity type the User wants to reference. The best I can come up with is something like:
/**
* #ORM\Column(name="plantType", type="integer")
*/
protected $plantType;
Then needing to duplicate this data in another function somewhere on the User class:
public function createNewPlant(){
if ($this->plantType == 0) return new Tree();
elseif ($this->plantType == 1) return new Bush();
}
This seems wrong to me since I'm mapping the integer value to the plant type in two places. Is there a better method?
Thanks a bunch!

Use permission/authorization checking scheme for object other than user?

I'm a bit new to this, and I haven't found a good way to make this work.
Lets say I have a user who is an employee of 1 company. This employee may decide to put his company's stocks for sales on the stock market.
Obviously we need to check for 2 permissions:
Does this employee have the right to put the stocks on stock market?
Are this company's stocks authorized to be put on the stock market?
The first check is simple, we simply use voter or ACL to do so. The second check is what I'm trying to figure out because so far in all the documents I have read the roles/permissions are only associated with user, not with arbitrary object (the company in this case)
Why not:
/**
* #ORM\Table(name="company")
* #ORM\Entity(")
*/
class Company
{
...
/**
* #ORM\Column(name="sellable", type="boolean", nullable=true)
*/
private $sellable;
...
}
Then something like:
if ($user->hasRightToSell() && $company->isSellable())?

Resources