The question is, do you need to pass a Job instance - as dependency - to the Person object, if it's only about the job name - like in the following context? I would say not really:
class Person {
/**
* Person name.
*
* @var string
*/
private $name;
/**
* Job name.
*
* @var string
*/
private $job;
/**
* Person's age.
*
* @var int
*/
private $age;
/**
* Person's place of birth.
*
* @var string
*/
private $birthPlace;
/**
* Job name.
*
* @param string $name Person name.
* @param string $job Job name.
* @param int $age Person's age.
* @param string $birthPlace Person's place of birth.
*/
public function __construct(string $name, string $job, int age, string $birthPlace) {
$this->name = $name;
$this->job = $job;
$this->age = $age;
$this->birthPlace = $birthPlace;
}
/**
* Get person's details.
*
* @return string
*/
public function getDetails() {
return $this->name . ', ' . $this->job . ', ' . $this->age . ', ' . $this->birthPlace;
}
}
use Person;
$person = new Person('TLG', 'programmer', 28, 'Paris');
echo $person->getDetails();
But what if more specific job details are to be assigned to a person? Then a Job instance can be used:
/*
* Job.
*/
class Job {
/**
* Job name.
*
* @var string
*/
private $name;
/**
* Job location.
*
* @var string
*/
private $location;
/**
*
* @param string $name Job name.
* @param string $location Job location.
*/
public function __construct(string $name, string $location) {
$this->name = $name;
$this->location = $location;
}
/**
* Get the job name.
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Get the job location.
*
* @return string
*/
public function getLocation() {
return $this->location;
}
}
/*
* Person.
*/
use Job;
class Person {
/**
* Person name.
*
* @var string
*/
private $name;
/**
* Job instance.
*
* @var Job
*/
private $job;
/**
*
* @param string $name Person name.
* @param Job $job Job instance.
*/
public function __construct(string $name, Job $job) {
$this->name = $name;
$this->job = $job;
}
/**
* Get person's details.
*
* @return string
*/
public function getDetails() {
return $this->name . ', ' . $this->job->getName() . ' in ' . $this->job->getLocation();
}
}
/*
* The call.
*/
use Job;
use Person;
$job = new Job('Programmer', 'London');
$person = new Person('John', $job);
echo $person->getDetails();
Notes:
- You should make all properties private. For the ones needed to be accessible from outside create public getters/setters, in order to ensure encapsulation.
- You should use namespaces. In this case, since both classes are defined in the global namespace, you don't need to import any classes, e.g. use the statements beginning with the use keyword. Though, in other situations, where defining top-level namespaces and subnamespaces is needed, then you should definitely make use of import statements. They will give you a very elegant way of manipulating class names and increase the readability of your class codes. See PSR-1: Basic Coding Standard, PSR-2: Coding Style Guide, PSR-4: Autoloader.
Now, what if a person can have multiple jobs? Then you could use a JobCollection:
/*
* Job.
*/
class Job {
// Same as above...
}
/*
* Job collection.
*/
use Job;
class JobCollection {
/**
* Jobs list.
*
* @var Job[]
*/
private $jobs = [];
/**
* Add a job.
*
* @return $this
*/
public function add(Job $job) {
$this->jobs[] = $job;
return $this;
}
/**
* Get all jobs.
*
* @return Job[]
*/
public function all() {
return $this->jobs;
}
}
/*
* Person.
*/
use JobCollection;
class Person {
/**
* Person name.
*
* @var string
*/
private $name;
/**
* Job collection.
*
* @var JobCollection
*/
private $jobs;
/**
*
* @param string $name Person name.
* @param JobCollection $jobs Job collection.
*/
public function __construct(string $name, JobCollection $jobs) {
$this->name = $name;
$this->jobs = $jobs;
}
/**
* Get the person name.
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Get the person's jobs.
*
* @return Job[]
*/
public function getJobs() {
return $this->jobs->all();
}
/**
* Get person's details.
*
* @return string
*/
public function getDetails() {
$result = $this->getName() . '<br/>';
foreach ($this->jobs->all() as $job) {
$result .= $job->getName() . ' in ' . $job->getLocation() . '<br/>';
}
return $result;
}
}
/*
* The call.
*/
use Job;
use Person;
use JobCollection;
// Create some jobs.
$job1 = new Job('Programmer', 'London');
$job2 = new Job('Bartender', 'Paris');
// Create the job collection and add the jobs to it.
$jobs = new JobCollection();
$jobs
->add($job1)
->add($job2)
;
// Create the person.
$person = new Person('John', $jobs);
echo $person->getDetails();
The Law of Demeter, as greatly presented in The Clean Code Talks - Don't Look For Things!, specifies that you should only inject dependencies which are directly used by a class. E.g. you shouldn't pass dependencies having the role of intermediaries, e.g. which are only used to create other objects whos methods you need to call.
In the earlier version of my answer I gave you the impression that it's false to use a Job instance in a Person object in order to assign the job name. It is correct to use a Job instance. By using it you are not breaking the LoD per se, but only the nuance of it, that you don't really need a Job instance in order to assign a job name to a Person object (as in my first example) if the job doesn't imply a more specifical case (as in the second example and in your good question in the comment).