I don't thinkTo allow the parse() function needs to have theparse both Document parameter because you can get to aand Document/Email from a Stream, you can create an interface, e. You just need to check if the stream was attached to Document or Emailg.
The alternative would be to have a separate parseDocument(Document $document) and parseEmail(Email $email)TextContainer. I think this approach is closer to your design where you have separate tables for that Document and Email implement. Something like:
interface TextContainer { function getText(): string; function setText(string $text); function save(); } class ApplyParsingRules { public function parse(Stream $stream, TextContainer $container) { $text = $container->getText(); $fields = $stream->fields()->get(); //1. get all $parsing_rules for each $fields. //2. parse $text by using each $parsing_rule //3. save the end result of $text by document/email specific and field. $container->setText($text); $container->save(); } } If you want to do processing based on Stream like in your example, I think the database design can be made clearer by removing the Documents and Emails tables and put the text inside Streams and add aactually prefer DocumentDataparse() to keep your Documents namereturn the result and path. This way,let the code doesn't needcaller decide what to care aboutdo with the distinctionresult, except when it needs to save/send which can be looked up quickly through a foreign key to DocumentDatai.e.
class ApplyParsingRules { public function parse(Stream $stream, TextContainer $container): string { $text = $container->getText(); $fields = $stream->fields()->get(); //1. get all $parsing_rules for each $fields. //2. parse $text by using each $parsing_rule return $text; } }