U moet de gebruikerscontrole in de join plaatsen, niet in de waar-voorwaarde, zoals dus:
SELECT c.course_id,l.topic,l.id,l.vid_duration,p.*
FROM courses c
LEFT JOIN lessons l ON l.course_id=c.course_id
LEFT JOIN progress p ON l.id = p.lesson_id and p.user_id = :userid
WHERE c.slug = :course
De reden hiervoor is dat JOINS de "left join" gebruikt (wat impliciet een outer join is". Dit type join betekent dat als de voorwaarde werkt, alle kolomgegevens voor die tabel voor die rij worden geretourneerd.. als het geheel voorwaarde niet werkt, retourneert het alle gegevens voor eerder genoemde tabellen, maar voor de tabel die in de rij wordt genoemd, retourneert het NULLS voor al die kolommen.
Mijn excuses voor die beschrijving, want het is moeilijk om precies onder woorden te brengen hoe een complexe outer (of left) join werkt zonder omslachtig te worden.