You could implement the strategy pattern, and set the strategy when you call shipmentCollection::make(). for example
ShipmentCollection::make($resultsFromFlatFile, FlatFileStrategy::class);
ShipmentCollection::make($resultsFromDB2, DB2Strategy::class);
ShipmentCollection::make($resultsFromAPI, APIStrategy::class);
the make() method would accept a second parameter called strategy and pass it to the trackingNumber method.
interface IStrategy {
public function getTrackingNumber($shipment);
}
class FlatFileStrategy implements IStrategy {
public function getTrackingNumber($shipment)
{
return $shipment['TrackingNumber'];
}
}
class APIStrategy implements IStrategy {
public function getTrackingNumber($shipment)
{
return $shipment->trackingNumber;
}
}
class DB2Strategy implements IStrategy {
public function getTrackingNumber($shipment)
{
return $shipment['SDDSC1'];
}
}
and then in the trackingNumber method of the collection would be:
private function trackingNumber($shipment, $strategy)
{
return (new $strategy)->getTrackingNumber($shipment);
}
if you want to remove the if-else but I believe it's a bit overkill. I believe it's all right to have that if else.
Note: You can also instantiate the strategy when you call Collection::make() and pass it as a parameter instead of instantiating it in Collection::trackingNumber() and it would probably be better because you are injecting the dependency, but it wouldn't make that big of a difference.
Note 2: This would work with the assumption you are creating the collections separately for every data source and not merging them before.