Sie müssen den Benutzer-Check in den Join setzen, nicht in die Where-Bedingung wie so:
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
Der Grund dafür ist, dass JOINS den "linken Join" verwenden (der implizit ein äußerer Join ist). Diese Art von Join bedeutet, dass, wenn die Bedingung funktioniert, alle Spaltendaten für diese Tabelle für diese Zeile zurückgegeben werden Bedingung nicht funktioniert, werden alle Daten für die zuvor erwähnten Tabellen zurückgegeben, aber für die in der Zeile erwähnte Tabelle werden NULLEN für alle diese Spalten zurückgegeben.
Ich entschuldige mich für diese Beschreibung, da es schwierig ist, genau in Worte zu fassen, wie eine komplexe äußere (oder linke) Verknüpfung funktioniert, ohne langatmig zu werden.