How to implement a MySQL fulltext search across multiple tables?

You should be able to add the tscore, ascore, and cscore values in the ORDER BY clause.

Try this:

SELECT *, 
  MATCH(books.title) AGAINST('$q') as tscore,
  MATCH(authors.authorName) AGAINST('$q') as ascore,
  MATCH(chapters.content) AGAINST('$q') as cscore
FROM books 
  LEFT JOIN authors ON books.authorID = authors.authorID 
  LEFT JOIN chapters ON books.bookID = chapters.bookID 
WHERE 
  MATCH(books.title) AGAINST('$q')
  OR MATCH(authors.authorName) AGAINST('$q')
  OR MATCH(chapters.content) AGAINST('$q')
ORDER BY (tscore + ascore + cscore) DESC

@Ike Walker's solution is great, however in my case I wanted to roll up the one-to-many results into a single row per search result. Riffing on @Ike Walker's solution here's how I got the job done:

Schema:

T1: Articles
T2: Comments (many comments to one article)

Indexes:

ALTER TABLE articles ADD FULLTEXT title_index (title)
ALTER TABLE articles ADD FULLTEXT body_index (body)
ALTER TABLE comments ADD FULLTEXT comment_index (comment)

SQL:

SELECT 
    articles.title, 
    SUM(MATCH(articles.title) AGAINST('$q') + 
    MATCH(articles.body) AGAINST('$q') + 
    MATCH(comments.comment) AGAINST('$q')) as relevance 
FROM 
    articles 
LEFT JOIN 
    comments ON articles.id = comments.article_id 
WHERE 
    MATCH(articles.title) AGAINST('$q') 
    OR MATCH(articles.body) AGAINST('$q') 
    OR MATCH(comments.comment) AGAINST('$q') 
GROUP BY 
    articles.id 
ORDER BY 
    relevance DESC

Note: If you want to add weights to each field you could do something like.

SUM((MATCH(articles.title) AGAINST('$q')*3) + 
        (MATCH(articles.body) AGAINST('$q')*2) + 
        MATCH(comments.comment) AGAINST('$q')) as relevance 

In this case title would have 3x, body 2x the value of a match in comments.