Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

A question about design: user class (and photo class?) 1

Status
Not open for further replies.

MelodyMaker

Programmer
Jun 17, 2008
11
CH
Sorry if I bother you with this, but I'm a OOP newbie and I would like to know your opinion about that.

I'm developing a site which requires registration and where users can upload their fotos. The question is this:


I created a user class

Code:

class User{
private $username;
private $email;
private $country;
??? private $photos= Array();???
...
function __construct($idUser) //here I load the details about the user;
}


Now, every user can upload at most 12 photos, which can have a small caption.
So, the question:

1) Do I need a "Photo" Class? (the encapsulation principle tells me so)
In this case what is the best way to associate this class to the user class? By storing the photo objects in an array property of the user class? And where should I put the method "loadPhotos"? In the user or in the photo class?

Code:

class Photo(){
private $url;
private $caption;
function __construct($IDPhoto){
.... here another select on the same table, mmm
}
}

...but if I do this, there will be two SELECT : One to get the user's ID photo and the other to retrieve photo's data. Or I'm wrong..?


2) Storing all the data about photos in an array (as a property) and forget about Photo class?

I'm a bit confused, sorry.

Thank you very much in advance.

Davide
 
you are on the right track. you would have a user class and a photo class. the user would contain a collection of photos.
Code:
class User
{
   int Id {get;}
   string Name {get; set;}
   void Add(Photo photo);
   void Remove(Photo photo);
   int NumberOfPhotos {get;}
}

class Photo
{
   int Id {get;}
   string Url {get; set;}
   string Caption {get; set;}

}
when you need to retrieve the user run a query
[tt]select * from user where userId = @id[/tt]
when you need to load the photos for the user load the user and then the photos.
[tt]select * from user where userId = @id
select * from photo where userId = @id[/tt]


Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Thank you!

The fact is that I really can't figure out, how the two classes are associated, I mean:
let's suppose that I give the user (it is so) the ability to add (upload) a photo and delete a photo.
Then, according to what you say, these methods should be part of the Photo Class.

which parameters should I pass to the AddPhoto? A reference to the user that owns that photo. So, first I have to instantiate User class, then the foto class (what about the parameters in its constructor?) and then, somewhat pass the userId to the photo Class?

Please not that my photo has only four attributes: a url, a caption, an idUser and a number (plus the methods to upload and delete, if I put them here).

Sorry, I know that I'm terribly confused...
 
I would keep the CRUD operations seperate from the domain objects.
So Select, Insert, Update, Delete would be handled by a different object(s). Either using a Repository or a DAO pattern for data access.

The photo class doesn't need a UserId field, rather it should reference the User object
Code:
class Photo
{
   string Url {get;set;}
   string caption {get;set;}
   int Number {get;set;}
   User AssociatedWith {get; set;}
}
the add and remove photo functions are part of the user. The User is the "aggregate" or "root" object in this scenario. the code would look something like this
Code:
class User
{
   private array photos;

   void Add(Photo photo)
   {
      photo.AssociatedWith(this);
      photos.Add(photo);
   }

   void Remove(Photo photo)
   {
      if(photos.Contains(photo))
      {
         photo.AssociatedWith = null;
         photos.Remove(photo);
      }
      photo.AssociatedWith(this);
      photos.Add(photo);
   }

   int NumberOfPhotos { get { return photos.Length; } }
}
with the logic encapsulated within the user class you can also add validation about how/when to add photos in one location. for example: a user can have no more than 12 photos.
Code:
void Add(Photo photo)
{
   if(NumberOfPhotos < 12)
   {
      photo.AssociatedWith(this);
      photos.Add(photo);
   }
}
if you need to know whether the photo was added or not you could query the NumberOfPhotos properies.
Code:
User user = UserRepository.GetById(id);
PhotoRepository.LoadPhotosFor(User user);

int numberOfPhotos = user.NumberOfPhotos;
user.Add(new Photo());
numberOfPhotos + 1 == user.NumberOfPhotos;
or change the signature of Add().
Code:
bool Add(Photo photo)
{
   if(NumberOfPhotos >= 12) return false;

   photo.AssociatedWith(this);
   photos.Add(photo);
   return true;
}

//usage example
User user = UserRepository.GetById(id);
PhotoRepository.LoadPhotosFor(User user);

int numberOfPhotos = user.NumberOfPhotos;
true == user.Add(new Photo());

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Jason, thank you very much!

Davide (from Lugano, Switzerland)
 
Jason, sorry, just another quick question. I need to let the user upload the foto. The method upload, where should it be implemented ?
In the Photo Class or in the User class?

Thank you very much in advance!
 
uploading a photo is a service. the user and photo domain objects are not aware of the "uploading" concept only how to add/remove photos. The term Upload is usually related to web programming. other than that you have a photo and user object and you want to related the two.

I would create a service facade to upload the photo. From your web page (desktop form, web service) you would collect the information into a DTO (data transfer object, simple light weight objects also known as POCO or POJO) pass this to the service. the service will query the repository/DAO to return a domain object, then add the picture to the user. then save the user with the pictures.
Code:
public class UserService
{
   void AddPhotoToUser(UserPhotoUploadDto dto)
   {
      User user = UserRepository.GetUserById(dto.UserId);
      PhotoRepository.LoadPhotosFor(user);
      user.Add(new Photo(dto.Url, dto.Caption, dto.Number));
      UserRepository.Save(user); // which will also save related entities i.e. photos
   }
}
within the webpage/form/presenter (if your using a MVP, MVC for the GUI interaction) you would call the service to save the image
Code:
int userId =  GetUserIdFromControlOrSessionOrVariable();
int theNumber = TheNumberToggleControl.Value;
string url = URLTextBox.Text;
string caption = CaptionRichTextBox.Text;

new UserService().AddPhotoToUser(new UserPhotoUploadDto(userId, url, caption, theNumber ));

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Jason, you helped me a lot. There's still another thing that comes from my unfamiliarity with java (I use PHP)

When you write statements like this one:

PhotoRepository.LoadPhotosFor(user);

What does exactly mean? Does it populate the "photos" array, by using internally a sort of for cycle?

Thank you in advance





 
Code:
PhotoRepository
{
   void LoadPhotosFor(user)
   {
      HashSet photos = RunQueryAgainstDBUsing(user.Id);
      foreach(photo in photos)
      {
           user.Add(photo);
      }
   }
   void RunQueryAgainstDBUsing(int id)
   {
       run the query
       "select * from photo where user_id = @id"
   }
}
if you are using java I would highly recommend Hibernate ORM for your data access needs. I use NHibernate for .Net and it's made my domain much more expressive and developer friendly. PHP has a PEAR project for data access, but I'm not too fimilar with it.

with Hibernate (at least NHibernate) you can take advantage of lazy loading child collections so all you need to do is fetch the user. when the photos are needed Hibernate just gets them. you don't need to explicitly load them. it also knows to automatically save changes when a "session" is complete

In your case this would mean eliminating the call to load photos and save the user. Hibernate would do that for you. so the code would look something like this.
Code:
ISession session = SessionFactory.OpenSession();
ITransacation transaction = session.BeginTransaction();
User user = session.Get(userId);
user.Add(new Photo(...));
transaction.Commit();
session.Flush();
I use c# not java, so there are probablly some java enhancements I missed. this is the general idea. There is a steep learning curve with Hibernate, but it's worth it.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Jason, I'm sorry for disturb you again, just a quick question:
do we use a photo repository for encapsulation purposes? I'm mean, what are the advantages to use a Photorepository?

Thank you very much for your help.
 
Repositories solve the problem of accessing persistant data while maintain the principle of Single Responsiblity.

user and photo are domain objects. they represent specific instances of objects within the system, they should only contain knowledge about how to interact with each other. the repository is responsible for loading domain objects from a persistant storage. usually a database, but it could also be a webservice, text file, etc.

the repository is resonsible for CRUD operations. Create, Create, Update and Delete. You will also see DAO (Data Access Object) which provide the same functionality as a repository.
Repositories are usually generic Repository.GetAnObjectBy(id), Repository.GetAListOfObjectsBy(critiera), Repository.DeleteObjectBy(id), etc. so all objects are persisted via this object.

DAOs are specific to the domain object UserDAO.GetUserBy(Id), UserDAO.GetUserWithPhotosBy(Id), Photo.GetPhotoBy(id)...

some like to a have a repository/DAO per object, others may have a repository/DAO per aggregate. depends how you structure your project.

I would highly recommend the book Domain Drive Design by Evans. the concepts apply to any OOP so it's language egnostic. Basically it's the building blocks for creating a Rich Domain Model. here is a link to the paraphrased version. give this a read through one evening. If you like it or confused by interested order the book.


Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top